Control flow - Xantier/nerd-stack GitHub Wiki

Application control flow

Rendering

Initial page loads go through the router file which then passes on the control to render/server file. This file renders the page using reacts renderToString, appends that rendered HTML to a Jade view and lets the router respond with that Jade view. Express Jade view engine then displays the page for the user. This flow will happen every time you enter an address or reload the page.

If the server router is not hit (ie. every interaction after initial page load) and we are working solely on the client then the application rendering goes through render/client file. This is a standard React component rendering.

The application uses react-router to determine which react component to render. There is a render/routes file that contains React-Router route definitions that determine the initial top level container and routes/url patterns which correspond to which components. This routes file is used on both client and server side rendering to determine what component needs to be rendered.

The data flow in the application uses React's Flux architecture. A standard simplification of this architecture is this picture from the Flux site: Facebook Flux architecture In our isomorphic application the architecture follows these same principles apart from initial data load on server side. Our application doesn't go through the dispatcher and stores to get the data on the server side.

Isomorpihc Flux Diagram

On initial load our server side render file calls out to a dataloader helper (modified from Ryan Florence's brilliant react-router-mega-demo). This dataloader receives information about the views that will be rendered and calls their static "load" method. The load method is included in each React view component that needs data to retrieve the first bit of initial data. This is done because the usual place where for example XHR requests would be made, "componentDidMount" method, is not called when React renders to string, ie. our server side rendering. This load method returns its set of data to our dataloader which completes when all data is loaded. After all data loads are finished our dataloader can pass the control back to our renderer.

Our server side renderer passes the data down in a context file as a prop to our wrapper element, in the original repo called "Index.JSX". This wrapper fills our react context with needed data. This data is then loaded on server side using our ContextMixin setting the state for our server rendered components. server renderer also dehydrates our loaded data and appends it to the rendered page so our client can grab it up and rehydrate its stores with that data, thus eliminating the need for an extra request for data on initial load.

After the initial page load is finished our client side scripts take control of the application again. Our client rendered rehydrates the data we have fetched while rendering the initial page on server side. This data is placed on our cache as a temporary solution. After rehydration our client renderer is called and the same data loading cycle starts again. The dataloader is called, which in turn calls each of the load methods in our views that usually fetch data. This time since we have rehydrated our token and data, we can check the cache if there is actually data before making XHR requests for the data. If data is found, we will just call our dispatcher with that cached data that we received from the server. If cache is empty, we will do an XHR request to fetch the data and then call our dispatcher with the data received from our XHR call.

After that our dispatcher fills our stores and our stores fire an event that they have some new data. Our client side views that are fishing for that event will retrieve the data and possibly re-render the view if there has been some changes to it.

Note: If you want to continue developing in an isomorphic way with the nerd-stack you need to remember to include a static load method to yours views. If your view has child views that also need to fetch their own data, you also need to include a static "children" array containing references to all children with their own load methods. In addition our context loading mixin is needed so that the context passed down from our wrapper object can be used to set the state of each component needing loaded data. Naturally if server side rendering is irrelevant to you these statics can be dropped and data fetching can be done in "componentDidMount" like Facebook's documentation suggests.

Also note that it's not possibly wise to use hugely nested children when retrieving data. A better solution is to use a parent object to fetch as much data as possible. Facebook's Flux overview defines views and controller-views in an application. Controller-view can be used to act as a data operator to receive data and forward it to its child views kinda like like old switchboard operators. For example Controller-views can be used to control the state of each child view that is attached to it. This way by passing down the state from one operator we can let React decide which views to re-render and which ones can be left as they were.