React JS Isomorphic Applications

Isomorphic Applications in React JS: React.js can be combined with Baobab so as to build true isomorphic apps for React.js. For one to inject state into an app, one can use either “state” or “context.” Let us learn these lessons in detail.

React JS Isomorphic Applications Example

Use of Props

Consider the code given below:

var Baobab = require('baobab');
var store = new Baobab({
items: []
}, {shiftReferences: true
}) ;
var ApplicationComponent = React.createClass({
render: function () {
return
<h1>Hi application, you are having
{this.props.store.items.length} left</h1>
);
}
}) ;
var render = function () {
React.render(<Application Component
store={store.get()})/>, document.body);
store.on('update', render);

In the above case, we have passed the complete store down to our components and in case the tree is changed, this will have to be passed down. Note that the references will be passed up the tree in case a change is made, meaning that “PureRenderMixin” can easily be used in our components.

Most of the sub-components contained in your app will need to access the stored object. The access can only be obtained in case the parent has passed it down. This is an indication that all of the components will have to pass the stored prop then down to the children. However, much work will be involved.

Use of “Context”

This is an alternative to the use of props. Consider the code given below:

var Baobab = require('baobab');
var store = new Baobab({
items: []
}, {
shiftReferences: true
}) ;
var W Component = React.createClass({
childContextTypes: {store: React.Prop Types.object},
getChildContext: function () {
return {store: this.props.store};
},
render: function () {return <Application Component/>
}
}) ;
});
var Application Component = React.createClass({
contextTypes: {store: React.PropTypes.object},
render: function () {
return
<h1>Hi application, you are having {this.con
text.store.items.length} left</h1>
);
}
}) ;
var render = function () {
React.render(<A Component
store={store.get()})/>, document.render);
};
store.on('update', render);

What we have done is that we have set a wrapper which will help us set the context for our app. Any component regardless of the parent can use the property “contextTypes” and then extract the store. With this, we will not have to pass our store down as properties.

The Injected State

The injection of the state will not be done similarly in both the client and the server. The Baobab tree will be injected in the client, but a plain object will have to be injected on the server. This is shown below:

The Injected State Client Code

var React = require('react');
var Baobab = require('baobab');
var Component = require('./Application Wrapper.js');
var store = new Baobab({items: 1}, {shiftReferences: true});
// As shown, we have not used store.get(), but we
have passed the actual //store
React.render(<Component store={store})/>,
document.render);

The Injected state Server Code

var React = require('react');
var W Component = require('./app/ApplicationWrapper.js');
We have created a normal object and then inject
var store = {afeias: []
};
// We will later show you what to do with this html
var html = React.renderToString(<WComponent store={store}}/>);

You should know how the wrapper works so as to expose the context. This is done similarly in both the client and the server-side of the app.

Wrapper Object Example

Consider the code given below:

var React = require('react');
var Application Component = require('./ApplicationComponent.js');
var W Component = React.createClass({
childContextTypes: {
store: React.PropTypes.object
},
getChildContext: function ({
return {store: this.props.store};
},
render: function () {
return <Application Component/>
}
}) ;
module.exports = WComponent;

The wrapper object will work so as to expose either the Baobab tree or the plain object. This will depend on whether we are doing it for the client or for the server.

To extract the State

A cursor object which has been defined in the component itself can be defined for the purpose of extracting the state. A new mixin has to be created since the Baobab tree the state is to be used somehow differently.

A pull request has been created for this purpose. This has also led to a different approach. An application event hub has been used for decoupling the business logic from the components. We now need to demonstrate how the mixin can be used on a component.

To extract the State Example

Consider the code given below:

var React = require('react');
var ContMixin = require('./ContextMixin.js');
var SmComponent = React.createClass({
mixins: [Cont Mixin),
cursors: {list: ['admin', 'list']
},
render: function () {
// Notice we have pointed to the state but not
the context
return
<h1>There exist (this.state.list.length} items
in the list</h1>
);
}
});
module.exports = SmComponent;

We are only depending on the mixin. After the injection of the state tree into the context of the app, we will have done away with the direct dependency on it. The mixin will then move the value from the context to the component’s state so as to give room to “PureRenderMixin” to perform its work.

PureRenderMixin Example

We now need to explain how the mixin works. The code is given below:

var React = require('react/addons');
// We should know whether we are on either the client or the server
var isBrowser = !(global && Object.proto
type.toString.call(global.process) === '[objectprocess)');
var ContMixin = {
// We should add PureRender Mixin for the optimized rendering
mixins: [React.addons.PureRender Mixin),
// grabbing the store from our context
contextTypes: {store: React.PropTypes.object
},
componentWillMount: function () {
if (This.context !this.context.store) {
throw new Error('A store has to be passed to the app wrapper');
}
// If no cursors are found, just return
if (!this.cursors) {
return;
}
// Preparing the map of listeners to the
// state update
this.subscriptions = {};
// we will then create the subscriptions to
the cursors so that
// the component will know about the changes
var comp = this;
var cSubscription = function (key, cursor) {
return function (value) {
var state ={};
state[key] = cursor.get();
comp.setState(state);
};
};
We can move through the cursors
var state = 0;
Object.keys(this.cursors).forEach(function
(cursor Key){
// In case we are in browser, the current state of the
// cursor can be moved over to the state object, by use of the key which
//has been defined on the cursor as the key on our state object.We will also
//register a listener for the state changes
if (isBrowser) {
var cursor = this.context.store.select(this.cursors[cursor
t(this.cursors[cursor Key]);
var cback = createSubscription(csKey,cursor)
state[casKey] = cursor.get();
this.subscriptions[casKey] = {
cursor: cursor,
callback: callback
};
cursor.on('update', callback);
// In case we are on the server, the same cursor path which has been
//defined will be use.
// Other than using the Baobab API, we canuse the path for drilling
// into our state object and set the state
} else {
var path = this.cursor[casKey];
var value = path.reduce(function (const Path,path Key, index) {
return contPath[pathKey);
}, this.context.store);
state[csKey] = value;
}
}, this);
// We can then put the state on the component
this.setState(state);
},
// Unregistering the listeners of state change
componentWillUnmount: function () {
Object.keys(this.subscriptions).forEach(func
tion (subscription) {
this.subscriptions[subscription].cur
sor.off('update', subscription.callback);
}, this);
}
};
module.exports = ContextMixin;

JSX File to use components on the server-side

The complete flow from the server to the client has to be well understood. The jsx file extension will have to be used, as the components will be used in the server too. With the use of the jsx extension, the node will only be able to transpile the jsx files, but not the normal js files. This is the file that we need to load.

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8"/>
</head>
<body>
{{APP}}
<script>
window.store = {{STORE}}
</script>
<script src="app.js"></script>
</body>
</html>

A placeholder should be created in which we will put our HTML for the app. The state that we have created should also be put in the global window object. This will allow the client store to grab the state so that we will be in sync.

Server.js Code

Consider the code given below for the file”server.js”:

// The jsx syntax has to be made available to the node
require('node-js').install({extension:'.jsx'})
var express = require('express');
var fs = require('fs');
var path = require('path');
var React = require('react');
// We then use the factory as the main server file since it does not support JSX, but only the required files
var Application Wrapper = React.createFactory
ry(require('./app/App Wrapper.jsx'));
// firing up the express server
var application = express();
// reading the contents of the HTML file we are having
var index = fs.readFileSync(path.re
solve(_dirname, 'index.html')).toString();
application.get('/', function (request, response) {
// we will first build up the contents of our store.This will be fetching the database stuff or whatever
buildStore Content()
then(function (store) {
// rendering the application to the HTML string, passing in our store
var application = React.renderToString(App=
application Wrapper({store: store}));
// replacing the placeholders with our app Html and the state of
// the store. You might want to load our store state with the ajax after the
//initial page load in the case it is quite large. You have to decide on this
//balance.
var html = index.replace('{{APP}}', app).re
place('{{STORE}}', JSON.stringify(store));
res.type("html");
res.send(html);
});
});
app.listen(3000);

Once the client has gone to “localhost:3000,” the HTML response will have the HTML of the app and a store object having the state which it is based upon. The “app.js” file will also be loaded. This file is a bundled one, and it is using either a web pack or browser. The code for the main entry file is shown below:

app.js Code

var React = require('react');
var Baobab = require('baobab');
// The component will use the contextMixin
var WComponent = require('./app/Wrapper Component.jsx');
1/ grabbing the state from our window
var store = new Baobab(window.store, {
shiftReferences: true
});

React.render(Component store={store}/>,document.render);

Once the file “app.js” has been launched, it will act in the same way as the server. However, the React.js will know that the current HTL (HTML Template Language) will be part of the application. The listeners will be wired up to the store and once the store has a change, an update will be made.

The good thing about this is that the components will listen completely to the components which have been decoupled from the parent components. This is an indication that the rendering of the app will be very much optimized.

To change the state of the store with actions

The state of the store should be changeable from within our components. It is not recommended that one gives a dependency to each component, and this is what we are in need of avoiding.

The context can be used. We will not be able to trigger the actions on the server-side. These will only be triggered once the user has interacted with the application or any other client module which interacts with the store such as a WebSocket service.

Wrapper and mixin Code

Let us now expand both the wrapper and the mixin so as to handle the actions:

var React = require('react');
var W Component = React.createClass({
childContextTypes: {
store: React.PropTypes.object,
actions: React.Prop Types.object
},
getChildContext: function () {
return {
store: this.props.store,
actions: this.props.actions
};
}
render: function () {
return <Application Component/>
}
});
module.exports = WComponent;

The code for your “Content Mixin” should be as follows:

Content Mixin Code

var React = require('react/addons');
var isBrowser = !(global && Object.proto
type.toString.call(global.process) === '[objectprocess)');
var ContMixin = {
mixins: [React.addons.PureRenderMixin),
// Adding the actions
contextTypes: {
store: React.PropTypes.object,
actions: React.PropTypes.actions
},
componentWillMount: function 01
if (!this.context !this.context.store) {
throw new Error('A store has to be passed to the app wrapper');
}
// referencing the actions on our component itself, to
// shortening the syntax. this.actions, instead of this.context.actions
this.actions = this.context.actions;
/.. rest of the mixin
},
componentWillUnmount: function () {...}
};
module.exports = Const Mixin;

At this point, we can pass our actions down the components. We are not expected to perform any changes to the server, as there will be no triggered actions at all. We should pass some actions to the client, and then look at the entry file once again.

app.js Code

This is shown below:

var React =require('react');
var Baobab = require('baobab');
var Component = require('./Wrapper Component.jsx');
// Let us look at the window.store
windows.store; // {list: []}
// grabbing the state from our window
var store = new Baobab(windows.store, {
shift Reference: true
});
// creating an action for updating the store
var actions = {
addItem: fanction (item) {
store.select('list').push(item);
}
};
//we then pass the actions down
React.render(<Component store%-D{stored} actions={actions}/>, document.render);

We have a component in the “Component” which is responsible for triggering the action “addItem.”

To “addItem” Code

Consider the code given below:

var React = require('react/addons');
var ContMixin require('./ContextMixin.js');
var LComponent = React.createClass({
mixins: [Cont Mixin, React.addons.Linked-
State Mixin),
cursors: {
// grabbing the list from our store and putting as "list"
// on our components state object. We have also listened to
// any changes made to the list value
list: ['list']
},
// returning an initial state for the input that
// will be an interaction of adding a new item to
// our list
getInitialState: function () {
return {
title: "
};
}
// We just point to this.actions and then run
// the method
addItem: function () {
this.actions.addItem(this.state.title);
this.setState({
title: "
});
},
renderItem: function (it) {
return <li>{it}</li>
},
render: function () {
return
<div>
<form onSubmit={this.addItem}>
<input valueLink={this.linkState('title')}/>
</form>
<ul>
{this.state.list.map(this.renderItem)}
</ul>
</div>
);
}
});
module.exports = List Component;

Now you are aware that the use of mixin can help you point to actions point to the component itself. If your application has more actions, these can be namespaced in the “this.actions.todos.add” or “this.actions.notifications.fetch.”That is how apps can be built.

Our aim is to inject some state into the server and then render the app there. However, we are also in need of allowing for highly optimized rendering on our client-side. The components are also expected to be very pure. The context that the components are running in helps them to know about other components. That is as much as we can say about this chapter.