Facebook's React library is a great choice for building isomorphic web UIs; it allows creating an in-memory representation of user interfaces, which looks like a tree of UI elements (components) and can be easily rendered either on a client or a server.
Let's do a simple exercise. If you were asked to create a JavaScript representation of a UI element such as a single tweet from twitter.com, how would you do it?
Here is how this UI element should look in a browser:
The HTML code for it would look similar to this:
<div class="tweet"> <div class="header"> <a class="account" href="/koistya"> <img class="avatar" src="/koistya/avatar.png" /> <strong class="fullname">Konstantin</strong> <span class="username">@koistya</span> </a> <small class="time">Jan 15</small> </div> <div class="content"> <p class="text">Hello, world!</p> <div class="footer">...</div> </div> </div>
Most likely, you would come up with a JavaScript object, which looks as follows (some code is omitted and replaced with ...
to keep it short):
const tweet = { node: 'div', class: 'tweet', children: [ { node: 'div', class: 'header', children: [ ... ] }, { node: 'div', class: 'content', children: [ { node: 'p', class: 'text', children: 'Hello, world!' }, ... ] } ] };
Having this object, you can easily write two render()
functions, one for Node.js, which will traverse this object and build an HTML string, and another one for a browser which will also travers this object and build a DOM tree (for example, using document.createElement(...)
).
In order to make this tweet object reusable, being able to display multiple tweets on a web page, you will want to convert it to a function, which will look similar to this:
function createTweet({ author, text }) { return { node: 'div', class: 'tweet', children: [ { node: 'div', class: 'header', children: [...] }, { node: 'div', class: 'content', children: [ { node: 'p', class: 'text', children: text }, ... ] } ] }; }
Now, you can construct a tweet object by passing some data to this function (props, in React terminology, and render it to HTML):
const tweet = createTweet({ author: ..., text: 'Hello, world!' }); const html = render(tweet);
React.js
takes this to the next level by abstracting all the complexities of this approach from you and providing a simple to use API with just a few public methods. For example, instead of writing your own render function, you can use:
// Node.js import ReactDOM from 'react-dom'; ReactDOM.hydrate(tweet, document.body); // Browser import ReactDOM from 'react-dom/server'; const html = ReactDOM.renderToString(tweet);
In a browser, React can detect changes made to the virtual representation of the UI, and synchronize it with the actual DOM in the most efficient way possible. So, you do not need to worry much (if at all) about mutating browser's DOM, but instead, can focus on creating UI elements of your app in a declarative manner (using JSX syntax) and managing application's state.
That's the library we are going to use for building a UI part of our isomorphic app.