Universal-React
**** Work In Progress ***
Universal/isomorphic/ssr(server-side rendering) react explained for my own educational purposes with the hope it will help others as well.
A lot has been written about this topic, you can easily find plenty of material out there, so I will try to summarize the essence of what I have learned with an emphasis on non-trivial scenarios.
Will evolve with time to full universal react app, so stay tuned.
Part of Dani Koren's personal blog.
Good to know
- For an excellent starting point to those who seek to understand the modern javascript technology stack click here
- For React code samples click here
- A nice wrap up about "Predictability and Side-effects" click here
Universal app - what is it?
To summarize this in one (long) sentence:
Building a fully featured client-side application that can be rendered on the server as well, and by fully featured we mean that it includes routing, async data fetching and state management.
So basically we pre-render our app on the server and serve the generated html to the client.
Universal app - why we need it?
- SEO - allows search engines to crawl your pages.
- Performance (initial load time).
- Cross device support.
- Same code base (sharing of logic).
- Non js users.
- Catching current trends ;)
In general, benefits for engineering, reusability, performance and SEO
Universal app - main concepts:
-
Universal rendering - using the renderToString() method which renders a component to its initial HTML
-
Universal routing - Associating views with routes both on the client and the server. We need to have the ability to render routes on the server (including components async data - see 3.1 section) before sending them on to the client. Options for universal routing:
Additional reading:
-
Universal data fetching - Retrieving data (usually via API) through both the client and the server
Additional options for Universal data fetching:
3.1 Handling async actions: -
It is very common for react components to depend on some async operation (usually to fetch data from some API), this can be a MobX action, a Redux action or any other mechanism that alters the global state.
There are many options to manage async actions on the server, it depends on the global state container (e.g. Redux/Mobx/Flux), additional libraries you may choose to use (e.g. redux-saga/redux-thunk/redux-observable/etc. for side effects management), and your app needs.
For a recap read the follow: 1. Asynchronous actions in Redux 2. Advanced Redux Action Types 3. Managing async actions - compare
-
Universal state management - Manage changes of state both the client and the server
Additional options for Universal state management: -
Universal mounting- mounting react with server-rendered components requires us to supply the same props both on the client and server.
we can do that by injecting the props into a script tag for client retrieval.
Universal app - walkthrough (with Redux):
- Handle the initial render when a user (or search engine crawler) first requests our app. When the server receives the request, it renders the required component(s) into an HTML string, and then sends it as a response to the client. From that point on, the client takes over rendering duties.
- Send the state of our app along in our response, so the client can use it as the initial state. if we preload any data before generating the HTML, we want the client to also have access to this data. Otherwise, the markup generated on the client won’t match the server markup, and the client would have to load the data again.
- Create a fresh, new Redux store instance on every request.On the client side, a new Redux store will be created and initialized with the state provided from the server.
- Optionally dispatch some actions.
- Pull the state out of store.
- Pass the state along to the client (dehydration - extract the current state of an app and serialize it into an object * ).
- Package & send the HTML to the client
- On the client side, a new Redux store will be created and initialized with the state provided from the server. Redux's only job on the server side is to provide the initial state of our app.
In the case of our components fetching asynchronous data, we need to pre-fetch that data before rendering on the server.
Due to renderToString() synchronous nature, we cannot use the recommended componentDidMount lifecycle method for ajax call, renderToString() simply won't wait for the ajax call to complete. A typical pattern is to add a static method named (usually) fetchData() on our top level components that deals with asynchronous data fetching.
Once react-router's match() method will match the specific route to it's designated location, we will iterate over the renderProps attribute, looking for that static fetchData() method, invoke it with a redux dispatcher, and wait for the promises to resolve.
Once all promises resolved we can render using renderToString().
Universal app - problems?
-
Complex server side code.
-
Huge performance hit on complex components renderToString() is synchronous, the server is blocked while it runs!
One can try and mitigate this by implementing cache techniques and Component Memoization techniques
You can also consider this: Server Side Render Caching + Profiling
Additional read on the subject:
React.js server-side rendering optimization with component memoization and templatization
Electrode modules
- Client code can break the server...
When we share the code base, there is always the possibility of client code breaking the server.
Universal react based Frameworks
Resources
Nice recap of the subject by walmart labs
Tips for Building Universal JavaScript Apps
Should I use React Server-Side Rendering?
#ToDo Will be implemented as separate branches:
-
Create simple implementation demonstrating universal react app.
-
Create Redux implementation demonstrating universal react + Redux app.
-
Create MobX implementation demonstrating universal react + MobX app.
-
Create Redux + async data fetching implementation demonstrating advanced universal react + Redux app.
-
Create MobX + async data fetching implementation demonstrating advanced universal react + MobX app.