Table of Contents

Web Reactive Programming

Hop.js Web Reactive Programming (WRP) enables DOM nodes components to be automatically updated or modified to reflect modifications of the program manipulated data. The components might be nodes attributes, nodes themselves that be can be modified or replaced, or even new nodes that can be added.

WRP ressorts on two components: the sources and the sinks. The sources are surpervised data structures whose modification yield to DOM updates. The sinks are the DOM components that are updated when sinks changes. They might be DOM nodes attributes and DOM nodes themselves.

Here is a first example, inspired by a React.js example that creates a web page showing the chronometers. The source is the state variable. The sink is the react node. The modification of the state variables that occur each seconds automatically yield to updating the web page.

stateful.js

service stateful() {
   return <html>
     ~{
        var state = hop.reactProxy( { secondsElapsed: 0 } );
        setInterval( function() { state.secondsElapsed++; }, 1000 );
     }

     <div>Seconds Elapsed: <react>~{ state.secondsElapsed }</react></div>
   </html>
}

A second example, also inspired by the React.js web site:

app.js

function TODOLIST( attrs, body ) {
   return <ul>
     <react>~{${attrs.items}.map( createItem )}</react>
   </ul>;
}

service todolist() {
   return <html>
     ~{
        function handleSubmit( e ) {
           e.preventDefault();
           state.items.push( { text: state.text, id: Date.now() } );
        }

        function onChange( e ) {
           state.text = e.target.value;
        }

        function createItem( item ) {
           return <li key=${item.id}> ${item.text} </li>;
        }
        
        var state = ( { items: new hop.reactProxy( [] ), text: '' } );
     }
        
     <div>
       <h3>TODO</h3>
       <TodoList items=~{state.items}/>
       <form onSubmit=~{handleSubmit( event )}>
         <input onchange=~{onChange( event )}/>
         <button>Add #<react>~{state.items.length + 1}</react></button>
       </form>
     </div>
   </html>
}

The third example, inspired by the Stratisfied Js web site:

ui.js

service ui() {
   return <html>
     ~{
        var R = hop.reactProxy( { min: 0, max: 100 } );
        var mid;
     }

     <body>
       <react> ~{
          if( R.min == R.max ) {
             return <div>Got it: ${R.min}!
               <button onclick=~{R.min = 0; R.max = 100}>again</button>
             </div>
          } else if( R.min == 0 ) {
             return <div>Think of an integer between ${R.min + 1} and ${R.max}
               <button onclick=~{
                  R.min = 1; R.max = 100;
               }>Ok
               </button>
             </div>
          } else {
             mid = R.min + Math.round((R.max-R.min)/2);
             return <div>Is it smaller than ${mid}?
               <button onclick=~{R.max = mid - 1 }>Yes</button>
               <button onclick=~{R.min = mid }>No</button>
             </div>
          }
       }
       </react>
     </body>
   </html>
}


Methods

Sources are implemented a subclass of the JavaScript proxy objects. As such only allocated objects can be used as sources, by opposition to numbers, booleans, undefined, null, or literal strings.

hop.reactProxy( object )

The hop.reactProxy method creates a reactive source out of object. Each modification to object will yield to updating the DOM nodes that depends on that object.

server.reactProxy( event, value )

The server.reactproxy method creates a reactive source out of a server event. Each time event is receive, the DOM is updated accordingly. The default value is used until event is received.

reactsrv/reactsrv.js

var clients = [];

service reactsrv() {
   clients.push( clients.length + ": " + Date() );
   hop.broadcast( "clients", clients );
   return <html>
     ~{ var conn = server.reactProxy( "clients", [] ); }
     <div># clients:
       <ol>
         <react>~{ conn.value.map( function( c ) {
            return <li>${c}</li>
         } ) }
         </react>
       </ol>
     </div>
   </html>
}

console.log( "Go to \"http://%s:%d/hop/reactsrv\"", hop.hostname, hop.port );

This example shows how to use WRP (Web Reactive Programming) to automatically update client interfaces upon server updates.

A reactor is created from the the server event clients. Each time the server broadcast this event, i.e., on each new connection, all the already connected clients are updated to reflect the number of connected clients and their connection times.

HTML tag

<REACT>

The body of a <react> node is client script that gets re-evaluated each time of its source is modified. The result of that evaluated in inserted in the DOM tree, possibly replacing already created dynamic nodes.

react/react.js

service react() {
   return <html>
     ~{
        var width = hop.reactProxy( { val: 2 } );
        var els = hop.reactProxy( [ "foo", "bar", "gee" ] );
     }
     
     <button onclick=~{width.val++ }>
       enlarge
     </button>
     <button onclick=~{els.push( "#" + els.length ) }>
       push
     </button>
     
     <table style=~{`border: ${width.val}px solid red`}>
       <react>
       ~{els.map( function( el ) { return <tr><td>1: ${el}</td></tr> } )}
       </react>
     </table>
     <table style=~{`border: ${width.val * 2}px solid green`}>
       <react>
       ~{els.map( function( el ) { return <tr><td>2: ${el}</td></tr> } )}
       </react>
     </table>
   </html>
}

console.log( "Go to \"http://%s:%d/hop/react\"", hop.hostname, hop.port );

This example illustrates Web Reactive Programming (WRP) in Hop. It creates two reactors (width and els) and two reactive HTML nodes (the two tables). When the width reactor is modified (via the enlarge button) the table borders automatically enlarge. When the els reactor is modified (via the push button), new elements are added to the two tables.