Read Effects from the DOM with Click Events

André Staltz
InstructorAndré Staltz

Share this video with your friends

Send Tweet
Published 6 years ago
Updated 3 years ago

So far we only had effects that write something to the external world, we are not yet reading anything from the external world into our app. This lesson shows how we can change the DOM Driver to return a "DOM Source" representing read effects, such as click events. We will leverage that to create an interactive application.

[00:00] Now we have a system with main and drivers where drivers are responsible for making effects happen. If you notice carefully, we only have effects that write something to the external world, we're not yet reading anything from the external world into our application.

[00:16] Eventually we need to do that. For instance, say you need to read from local storage or you need to receive user events such as clicks. All of these are read effects that the main should be able to take in. Right now main is a function that only returns an output but it doesn't take any input.

[00:34] Also notice that drivers are functions that have an argument, take that input, but they don't return anything yet. That's because the output of the main is the input to a driver. We're going to have to have the opposite, where the output of a driver is the input to main.

[00:54] I'm going to call this sources. That's our input to our program. It allows us to read from the external world. We're going to have to return from the DOM driver a domSource. We're going to see later how to create that domSource somehow.

[01:13] Sources and sinks are a name that come from dataflow terminology. Basically, source means an input or a read effect. Sink means an output or a write effect. It makes a lot of sense here for our main, where inputs are things that we are going to read and outputs are things that we are going to write.

[01:38] For instance, if you want to read clicks happening on the DOM that should be available under sources.DOM and we're going to call that a click stream. The click stream will be created by the DOM driver, and that's what here should be the domSource.

[01:54] We can create that with fromEvent document and type click. This will give us a stream of clicks happening on the DOM.

[02:04] Now as you see, the DOM driver takes an input and returns something and also does the main taking input and return something. We need to plug these two together, DOM driver and main, and we do that in the run, just like we had before.

[02:21] I'm going to simplify this code just by a hard coding it for now to keep it simple. We know that sinks.DOM has our messages that we should write to the DOM, so we're going to call the DOM driver on that.

[02:38] We know that the DOM driver now returns something which is the domSource. We want to pass the domSource to the main function. I'm just going to pass it like that.

[02:49] Now we hit a problem with JavaScript, because domSource is not available when we called this line, because domSource is only created after. We can't actually swap this order, because then we get the same problem because sinks is not yet available.

[03:08] Let's try to simplify this problem by writing here comments that b is f(a) and a is g(b). In order to create b we need a, and in order to create a we need b. As you can see there is a dependency in between these two parts. That's actually where the name cycle.js comes from because there is a circular dependency between these two parts.

[03:32] We can't actually solve this as it is in JavaScript, but we can apply a trick and that's how we're going to solve it in practice. First we're going to create a fake a somehow, and then we can pass our fake a to f. Then we get the real b. Once we have the real b we can get the real a. Now that we have real a and fake a, we can say, "Fake a starting behaving as if you would be the real a."

[04:01] How we do that with streams is that first we're going to create a fake DOM sink and that's going to be extreme create. Once we have that we can pass it here to the DOM driver, fake DOM sink. Then we get the real domSource.

[04:16] Once we have the real domSource we can get the real sinks. Then we can say, "Fake DOM sink please start imitating the real DOM sink," like that. This is a method in extreme, which mutates or changes the behavior of the fake DOM sink.

[04:35] Now run is able to plug together the main function with the drivers even if there is a circular dependency between these two parts. That means that we can get the input one to be the output of the other and vice versa. It means that we actually have a click stream here now that we can use in our application.

[04:54] We're going to create a restart feature in our app by applying map each click to this counting part here, like that, and then we can apply flatten. We're also going to start with a fake click just so that initially we restart, which means just basically start. This will be able to create a restart functionality, so that once I click it will restart the counting.

[05:33] Finally our application can read and write to the DOM. We achieved all of this with some messy code here, hard-coded inside run but we're going to see how to fix that later.

~ 5 years ago

You may be an exceptionally talented developer, but you might be an even more exceptional teacher. I love watching your videos. The way you build abstractions from the ground up is brilliant.

André Staltz
André Staltzinstructor
~ 5 years ago

Many thanks! :)

~ 5 years ago

FWIW, in this video, Andre uses RxJS v4... this code won't behave properly with RxJS v5. Here's a RxJS v5 snippet that works:,console,output

Steve Lee
Steve Lee
~ 5 years ago

Note that fromEvent() is added to JSBin via a <SCRIPT>

fromEvent() and other xstream extras are described in the extras documentation. The source can be found in the same repo.

Laszlo Vass
Laszlo Vass
~ 2 years ago

Please note, that in order the code to work on Plunker (or in your browser) you must use the legacy (0.21.6) version of SystemJS. For that just modify the script tag in the index.html like below:

<script src="">