-
Notifications
You must be signed in to change notification settings - Fork 8
Radioactive Tutorial
$ bower install radioactive$ npm install radioactiveradioactive.react is the entry point to the radioactive environment.
Just pass a javascript function to radioactive.react()
and see what happens.
radioactive.react(function(){
console.log("Hello World");
})In this case the code will run from top to bottom. Once. And that's it. But that's because there is nothing reactive about the code we just passed to radioactive.react.
Let's see what happens if we pass it a "reactive" block of code. A block of code becomes reactive if it has any reactive functions in it. Let's use a the built in radioactive.time(). It is a function that simply returns the time. But it is also reactive: Its value changes every 1000ms aprox.
radioactive.react(function(){
console.log("The time is " + radioactive.time() );
})The previous snippet will run once. And twice... and keep on running forever. Printing "The time is 00000000" over and over again every one second.
This is because radioactive.time() is reactive. Which is a fancy way of saying that it notifies when its value changes. Actually. It doesn't notify "us". It talks directly to radioactive.react. But we don't need to deal with that. From our point of view it is just a plain javascript function.
In fact. If we call it outside of the reactive context and it will work just fine. The notification process does not interfere with normal Javascript. It is only active when necessary.
console.log("The time is " + radioactive.time() );You just learnt the first two rules of reactive datasources
Rule 1: All reactive datasources are valid javascript functions
Rule 2: All functions are valid reactive datasources. But only some of them know how to notify changes
And since datasources are just functions you can use them to do whatever it is you can do with a function. For example, we can call our datasource within another function.
function tomorrow(){ return radioactive.time() + 1000 * 60 * 60 * 24 }
radioactive.react(function(){
console.log("Let's go our for lunch. How about " + tomorrow() + " ?" );
})Notice how the tomorrow() function inherited the reactive nature of radioactive.time(). This is a very simple example ( one level deep, one nested function ) but the same is true for any number of levels and any number of combined reactive datasources. Reactivity "bubbles up the stack" automatically from one function to the next.
Rule 3: Reactivity is transparently transitive across function boundaries
These three rules are what make Radioactive's implementation of reactivity so powerful. You can completely forget about keeping track of which functions are reactive datasources and which ones are not. You just "write code". And then you let the radioactive.react() loop figure out what to do and when.
In fact. The boundary of where a reactive datasource starts and ends are blurry. If you compose two or three datasources, or if you nest a datasource within a function, the outermost function is itself a datasource (?).
Datasource is, in fact, just a name to identify functions whose main job is to return data and that no side effects. Which reminds me of another rule:
Rule 4: Reactivity should only be used on side-effect free functions. Radioactive is not meant for orchestrating code that modifies application state.
How you use the loop is up to you. But a really common use case is to update the UI. If you are using jQuery, then you already know what to do:
radioactive.react(function(){
$("#time").text("The time is " + radioactive.time() )
})Care must be taken to put all side-effecting code at the end of the block. You will understand why in a while.
You may be asking yourself: How do I stop a radioactive.react() loop?
var limit = 10;
radioactive.react(function(){
if ( limit-- == 0 ) radioactive.stop();
console.log("The current time is " + radioactive.time() );
})Or alternatively:
var stopper = radioactive.react(function(){
console.log("The current time is " + radioactive.time() );
})
setTimeout( stopper , 10000 );However. Don't worry about stopping loops for now. You rarely have to and we will take a deeper look at the Reactive Loop Lifecycle in a while.
But first, let's get to the fun part: Using async and streaming datasources.
An async datasource is anything that takes a callback and returns a value ( or an error ) at a later point in time. For example an AJAX call to a remote server.
As we promised, Radiaoctive allows you to see all types of datasources as synchronous javascript functions. And this applies to async functions as well of course.
The first thing we need to do is to transform our async datasource into a reactive function. There are many ways of doing this.
- Use radioactive.data, which provides ready to use integrations with common datasources ( such as JSON + AJAX )
- Use radioactive.syncify to transform an async datasource into a reactive function.
- Use a specialized third party module for the library at hand ( like node's http, or jQuery ajax )
radioactive.react(function(){
console.log( radioactive.data("http://foo.com/bar.json") );
})This is a very general technique. Once you see how easy syncify is you will be ready to integrate other async datasources.
In this case, let's create a syncified reactive service on top of jQuery.ajax.
// create a reactive service that calls jQuery AJAX under the covers
// Using radioactive.syncify to wrap async services like this is a common pattern
var json = radioactive.syncify(function( url, cb ){
$.ajax( { dataType: "json", url: url, success: function( data ){ cb(null, data) } } )
})
radioactive.react(function(){
var data1 = json("").name
// TODO: find an interesting example
})Notice how we are calling async services without using callbacks.
At this point, a little reminder is in order:
Only use Radioactive to work with services that READ data
The reason for the Read-only limitation is that you have no control as to how many times each service will be invoked under the covers. Or even when it will be invoked.
Note: radioactive.syncify also supports services that return a valid Promises/A+ "thennable" object ( it must have a .then() method ).
Another common source of data is one that is constantly changing. This could be the data from a text input, a remote API like Firebase, a Websocket connection, etc.
These kinds of datasources can also be brought into the Radioactive environment in a very transparent way. In this case we will use the radioactive-firebase module.
bower install radioactive-firebaseThis module has a very minimal API. It simply adds .get() method to the Firebase class. This method let's you use any Firebase reference as a reactive datasource.
radioactive.react(function(){
})Common sources of local data are:
- Mutable state that you need to 'react' to ( ex: the currently logged in user )
- Streams of data ( ex: the value of an HTML Text Input )
Cells are radioactive's version of variables. A cell can store one value at a time. What's great about cells is that they are reactive datasources. Once their value changes, they notify. If you keep your mutable state on cells then your radioactive.react() loops will pick up any changes.
// create two cells initialized to string values
var name = radioactive.cell('Bob')
var lastname = radioactive.cell('Marley')
// use them inside a reactive loop
radioactive.react(function(){
console.log( name() + " " + lastname() )
})
// change the value of name in 5 seconds
setTimeout( function(){
name("Homer")
}, 5000 )The API of a cell is very simple. But may seem strange at first.
- In order to set a value, you call it as a function with one parameter:
cell( value ) - In order to access this value, you call it as a function with no parameters:
cell( )
Why not use cell.get() and cell.set()? Well. It turns out there is actually a very good reason for this. But that's a longer story. For now, suffice to say that cells were designed this way to help you build better apps.
Cells are probably the single most useful feature of the library. You will find yourself using them all the time. I highly recommend you read more about them later on.
Reading data from the UI is definitely one of the most common use cases and there are many ways to go about this:
- Using a pre-existing integration. For example radioactive-jquery
- Doing it manually using cells
Let's explore all of them.
Whenever you need to integrate something into radioactive the catch-all solution is to put all the data into a radioactive.cell and let the cell do the heavy lifting. In fact, this is what several of the radioactive modules do under the covers and is a recommended best practice.
Let's use a cell to create a streaming datasource of values coming from a text input.
var name = radioactive.cell("", { throttle: 300 });
$("#name-input").on("keyup", function(e){
name( $(this).val() );
});Notice that we are setting the cell's trottle to 300ms. This helps us restrict the amount of change notifications that the cell will send.
Now we can use the cell as a datasource within a reactive loop:
radioactive.react(function(){
console.log( "Your name is " + name() );
})Radioactive is deceptively simple. But it packs a lot of power. You can leverage it as a replacement for flow control libraries such as async, Step, etc.
Let's go back to using the json() reactive service we built at the beginning of this tutorial.
radioactive.once(function(){
var name = json("http://.../.json")
var lastname = json("http://.../.json")
return name + " " + lastname;
}, function( e, r ){
})The radioactive.once() method allows you to run a block of code from top to bottom. You can pass a callback to receive the return value.
Just as with radioactive.react, within this block you can any type of datasource including, obviously, async datasources.
The above would be the equivalent of calling two async functions one after the other. But we don't have to deal with callbacks, events, etc. From our POV, they are just functions.
What if you were to throw in a Firebase reference in there, or any other datasource? It would just work. Radioactive will wait until all the data arrives.
I hope you can see just how powerful this is.
But wait. It gets better!
What if you wanted to run a few async calls in parallel? Enter radioactive.fork:
radioactive.react(function(){
var fork = radioactive.fork();
var data1 = fork(function(){ return json() })
var data2 = fork(function(){ return json() })
fork.join()
console.log( data1 + " " + data2 )
})- Radioactive Tutorial
- API
- working with data
- publishing datasources
- utility
- Typescript Definition