Inside of each component you have to define a function, let's name it as loadData. this function will do async work of your component.
when your server gets a request, you have to look at that Url and then you have to decide which components to render.
For each component that needs to be rendered, we will call that loadData function that is attached to each of components to initiate the data loading process. The key here is that we are not doing some initial render of the application. We just have a set of components. Each one says “here is a resource that I need”. Then whenever someone makes a request, we look at the set of components that we should need to render to get the page to show up. Then we will take all those components, we will take these little data loading requirement functions that are attached to them and we will call each of those. All these dataLoad functions return promise, so we have to detect all of them resolved before we render our app.
Let's say we have Users.js so we define our function as follow:
const loadData = store => {
return store.dispatch(fetchUsers());
};
when we import our component, we import inside an object.
export default {
loadData:loadData,
component: connect(mapStateToProps, { fetchUsers })(UsersList)
};
We have to set up our Routes.js file with the help of react-router-config.
Routes.js
import React from "react";
import Home from "./pages/Home";
import Users from "./pages/UsersList";
import App from "./App";
import NotFoundPage from "./pages/NotFoundPage";
export default [
{
...App,
//App component will be shown inside each component.
//array of routes will be used inside the App.js down below
routes: [
{
path: "/",
...Home,
exact: true
},
{ ...UsersList, path: "/users" },
{ ...AdminsListPage, path: "/admins" },
{ ...NotFoundPage }
]
}
];
Here is the App.js
import React from "react";
import Header from "./components/Header";
import { renderRoutes } from "react-router-config";
import { fetchCurrentUser } from "./actions";
//this component is to render components that each component uses in common
//props.route is the child components that are passed from Routes.js
const App = ({ route }) => {
return (
<div>
<Header />
{renderRoutes(route.routes)}
</div>
);
};
export default {
component: App,
//this function is get called by redux so store is passed in
//as you might know Header should always know about authentication status
//we populate the store with the authentication info so Header can use it.
loadData: ({ dispatch }) => dispatch(fetchCurrentUser())
};
now we set up all the loadData functions, now it is time to invoke them when our server gets request. for this we will be using matchRoutes
function from react-router-config.
app.get("*", (req, res) => {
const store = createStore(req);
//express does not touch routing. it delegates everything to react.
//I will just place the logic behind invoking loadData functions here.
//matchRoutes will look at the path and will return the array of components to be loaded.
//this is what matchRoutes function show `{ route: { loadData: [Function: loadData], path: '/users', component: [Object] },//component that we show
match: { path: '/users', url: '/users', isExact: true, params: {} } }
`
//
const promises = matchRoutes(Routes, req.path)
.map(({ route }) => {
return route.loadData ? route.loadData(store) : null;
}) //we got the array of loadData functions now we are invoking them
.map(promise => {
if (promise) {
return new Promise((resolve, reject) => {
promise.then(resolve).catch(resolve);
});
}
});
//Promise.all takes an array of promises and resolves when all of the items resolve
Promise.all(promises).then(()=>{
//here is when it is time to render your server-side code.
}).catch(e => console.log(e.message));
}