This is example from official docs (https://reacttraining.com/react-router/web/guides/server-rendering/data-loading):
import { matchPath } from 'react-router-dom'
// inside a request
const promises = []
// use `some` to imitate `<Switch>` behavior of selecting only
// the first to match
routes.some(route => {
// use `matchPath` here
const match = matchPath(req.url, route)
if (match)
promises.push(route.loadData(match))
return match
})
Promise.all(promises).then(data => {
// do something w/ the data so the client
// can access it then render the app
})
This documentation makes me very nervous. This code doesn't work. And this aproach doesn't work! How can I preload data in server?
This Is what I have done - which is something I came up with from the docs.
routes.cfg.js
First setup the routes config in a way that can be used for the client-side app and exported to used on the server too.
export const getRoutesConfig = () => [
{
name: 'homepage',
exact: true,
path: '/',
component: Dasboard
},
{
name: 'game',
path: '/game/',
component: Game
}
];
...
// loop through config to create <Routes>
Server
Setup the server routes to consume the config above and inspect components that have a property called needs
(call this what you like, maybe ssrData
or whatever).
// function to setup getting data based on routes + url being hit
async function getRouteData(routesArray, url, dispatch) {
const needs = [];
routesArray
.filter((route) => route.component.needs)
.forEach((route) => {
const match = matchPath(url, { path: route.path, exact: true, strict: false });
if (match) {
route.component.needs.forEach((need) => {
const result = need(match.params);
needs.push(dispatch(result));
});
}
});
return Promise.all(needs);
}
....
// call above function from within server using req / ctx object
const store = configureStore();
const routesArray = getRoutesConfig();
await getRouteData(routesArray, ctx.request.url, store.dispatch);
const initialState = store.getState();
container.js/component.jsx
Setup the data fetching for the component. Ensure you add the needs
array as a property.
import { connect } from 'react-redux';
import Dashboard from '../../components/Dashboard/Dashboard';
import { fetchCreditReport } from '../../actions';
function mapStateToProps(state) {
return { ...state.creditReport };
}
const WrappedComponent = connect(
mapStateToProps,
{ fetchCreditReport }
)(Dashboard);
WrappedComponent.needs = [fetchCreditReport];
export default WrappedComponent;
Just a note, this method works for components hooked into a matching routes, not nested components. But for me this has always been fine. The component at route level does the data fetch, then the components that need it later either has it passed to them or you add a connector to get the data direct from the store.