How to logically combine a reactive router and reduction for visualization on the client side and on the server side

I would like my React-based SPA to appear on the server side (who is not these days). Therefore, I want to combine React with react-router , redux and some construction layer, for example, isomorphic starters .

There is a hapi universal redux that brings everything together, but I'm struggling with how to organize my flow. My data comes from several REST API endpoints. Different components have different data needs and must load data right on the client. Instead, all the data for a specific route (set of components) and the necessary components transferred to the rows should be extracted on the server.

In my first approach, I used redux middleware to create asynchronous actions that load data, return a promise, and trigger the SOME_DATA_ARRIVED action when the promise resolves. Gearboxes then update my store, remake components, all is well. Basically, it works. But then I realized that the flow becomes uncomfortable at the moment when routing comes into play.

In a component that lists multiple data records, there are several links for filtering records. Each filtered dataset must be accessible through its own URL, for example /filter-by/:filter . Therefore, I use different <Link to={...}> components to change the URL when I click and start the router. The router must update the repository, then in accordance with the state represented by the current URL, which in turn causes a re-rendering of the corresponding component.

It is not easy to achieve. I first tried componentWillUpdate call an action that asynchronously loaded my data, populated the store, and caused another re-rendering cycle for my component. But this does not work on the server, since only 3 life cycle methods are supported.

So I'm looking for the right way to organize this. User interactions with an application that change the state of applications from the point of view of users must update the URL. IMO this should force the router to somehow load the necessary data, update the storage and start the negotiation process.

So interaction -> URL change -> data fetching -> store update -> re-render .

This approach should also work on the server, because you need to determine the loaded data from the requested URL, generate the initial state and pass this state to the store reduction generation. But I do not find a way to do it right. Therefore, the following questions arise for me:

  • Am I mistaken because there is something I donโ€™t understand / donโ€™t know yet?
  • Is it right to store data downloaded from the REST API in the store abbreviation?
  • Isn't it hard to have components that save state in store reductions, while others control their state by themselves?
  • Is the idea of โ€‹โ€‹having interaction -> URL change -> data fetching -> store update -> re-render just wrong?

I am open to all kinds of suggestions.

+6
source share
1 answer

Today I installed exactly the same. What we already had was a jet router and a reduct. We modulated some modules to introduce things into them - and viola - this works. I used https://github.com/erikras/react-redux-universal-hot-example as a link.

Parts:

1. router.js

We return a function (location, history, store) to configure the router with promises. routes is the route definition for a reaction router that contains all of your components.

 module.exports = function (location, history, store) { return new Bluebird((resolve, reject) => { Router.run(routes, location, (Handler, state) => { const HandlerConnected = connect(_.identity)(Handler); const component = ( <Provider store={store}> {() => <HandlerConnected />} </Provider> ); resolve(component); }).catch(console.error.bind(console)); }); }; 

2. store.js

You just pass the initial state to createStore(reducer, initialState) . You just do it on the server and on the client. For the client, you must make the state available through the script tag (i.e. window.__initialstate__ ). See http://rackt.imtqy.com/redux/docs/recipes/ServerRendering.html for details.

3. server rendering

Get the data, configure the initial state with this data ( ...data ). createRouter = router.js above. res.render is an expression creating a jade pattern with the following

 script. window.csvistate.__initialstate__=!{initialState ? JSON.stringify(initialState) : 'null'}; ... #react-start != html 

 var initialState = { ...data }; var store = createStore(reducer, initialState); createRouter(req.url, null, store).then(function (component) { var html = React.renderToString(component); res.render('community/neighbourhood', { html: html, initialState: initialState }); }); 

4. customer adaptation

Then your client can do basically the same thing. location can be HistoryLocation from React-Router

 const initialState = window.csvistate.__initialstate__; const store = require('./store')(initialState); router(location, null, store).then(component => { React.render(component, document.getElementsByClassName('jsx-community-bulletinboard')[0]); }); 

To answer your questions:

  • Your approach seems right. We do the same. You can even include the URL as part of the state.
  • All the state inside the redux store is a good thing. Thus, you have one source of truth.
  • We are still developing what should go where right now. We are currently requesting data on componentDidMount on the server, it should already be there.
+3
source

All Articles