Reputation: 347
i have a working client side app which uses redux-router
.
i dispatch the initial state of user
page from my api.
my routes file:
export default function ({ dispatch, getState }) {
function getUser(nextState, replaceState) {
dispatch(getUserData(nextState.params.id));
}
return (
<Route path="/" component={App}>
<Route path="user/:id" component={User} onEnter={getUser}/>
<Route path="*" component={NoMatch}/>
</Route>
);
}
}
so on the client it works great.
i would like to get my markup rendered-to-string after the getUserData
dispatch is back with data.
thats my server matching and rendering (from the official server-rendering example):
app.use((req, res) => {
const store = reduxReactRouter({ routes, createHistory: createMemoryHistory })(createStore)(reducer);
const query = qs.stringify(req.query);
const url = req.path + (query.length ? '?' + query : '');
store.dispatch(match(url, (error, redirectLocation, routerState) => {
if (error) {
console.error('Router error:', error);
res.status(500).send(error.message);
} else if (redirectLocation) {
res.redirect(302, redirectLocation.pathname + redirectLocation.search);
} else if (!routerState) {
res.status(400).send('Not Found');
} else {
res.status(200).send(getMarkup(store));
}
}));
});
is it possible to get this working while using onEnter
?
or it only fires on the browser?
would appreciate any help guys! thanks
Upvotes: 1
Views: 154
Reputation: 276306
I don't understand - I assume getUser is async, rendering is always synchronous in React. You'd have to get the data ahead of time. Relying on onEnter
is a nice idea but it wouldn't work.
Basically...
First: you need to expose your methods that grab data so they can be called on the server outside of flux. The action/dispatcher/store doesn't work well on request/response cylce and make sure you have a file with API calls.
Second: put all your routes in a JSON file, React Router (or whatever router) reads that JSON and adds the routes in a loop with their handler, the server-side code reads those routes and adds express routes for the same routes pointing to the method, each route in the JSON also contains a reference to the data the component needs to a real initial render (what async calls). You create an empty copy of the initial state object, each handler on the express side performs all the calls to the relevant API methods (from the JSON) and when a Promise.all
resolves on "getting the data and filling it in the state" resolves you render,
The render now contains the relevant data.
Third, you need to figure out how to pass state to the server, how the user is logged in, what they can do and so on - I recommend having a second server act as a cache since rendering is CPU bound in React so you need caching. We cache based on route, user status, device and a few other basic things.
Fourth, you point your non-rendering server (the caching one) to the rendering one and forward requests, hopefully requests should hit the cache, you need to have the non-rendering server "fall back" to client-side rendering only if the rendering server fails. This also lets you "hot swap" the rendering server on deploy.
You also need some way to deliver the JavaScript itself from the rendering server (so there is a single source of truth) that shouldn't be too big of an issue.
There are a lot more delicate parts in the flow - but that's pretty much how we do it and we have sub-second full renders which I think is nice. Went down from 5 seconds when our site was mostly Angular and rendered on the client.
Upvotes: 1