(Universal React + redux + response-router) How do I avoid re-fetching route data when the browser boots up?

I am using the static fetchData method for my Route component ...

 const mapStateToProps = (state) => ({ posts: state.posts }) @connect(mapStateToProps) class Blog extends Component { static fetchData (dispatch) { return dispatch(fetchPosts()) } render () { return ( <PostsList posts={this.props.posts} /> ) } } 

... and collecting all the promises before the server-side source render ...

 match({ routes, location }, (error, redirectLocation, renderProps) => { const promises = renderProps.components .filter((component) => component.fetchData) .map((component) => component.fetchData(store.dispatch)) Promise.all(promises).then(() => { res.status(200).send(renderView()) }) }) 

It works fine, the server waits until all my promises are resolved before the application is rendered.

Now, on my client script , I am doing something similar, like on a server ...

 ... function resolveRoute (props) { props.components .filter((component) => component.fetchData) .map((component) => component.fetchData(store.dispatch)) return <RouterContext {...props} /> } render(( <Provider store={store}> <Router history={browserHistory} routes={routes} render={resolveRoute} /> </Provider> ), document.querySelector('#app')) 

And it works great. But, as you can subtract, when initially rendering the page, static fetchData gets called twice (once on the server and once on the client), and I don't want that.

Any suggestions on how to solve this? Recommendations?

+7
reactjs redux react-router redux-thunk
source share
3 answers

I print this from my phone, so I apologize for the lack of formatting.

In my project, I am doing something similar to you; I have a static fetchData method, I loop through the components from renderProps and then call the static method and wait for promises to resolve.

Then I call the get function from my redux repository, string it and pass it to my render function on the server so that it can display the original state object on the client.

From the client, I just take this inital state variable and pass it to my redux store. Redux then processes the access to your client repository according to the one on the server. From there, you simply transfer your store to the supplier and continue as usual. You do not need to call your static method on the client at all.

As an example of what I said, you can check out my github project as the code explains itself. https://github.com/mr-antivirus/riur

Hope this helps!


[Edit] Here is the code!

Client.js

 'use strict' import React from 'react'; import { render } from 'react-dom'; import { Provider } from 'react-redux'; import { Router, browserHistory } from 'react-router'; import createStore from '../shared/store/createStore'; import routes from '../shared/routes'; const store = createStore(window.__app_data); const history = browserHistory; render ( <Provider store={store}> <Router history={history} routes={routes} /> </Provider>, document.getElementById('content') ) 

Server.js

 app.use((req, res, next) => { match({ routes, location:req.url }, (err, redirectLocation, renderProps) => { if (err) { return res.status(500).send(err); } if (redirectLocation) { return res.redirect(302, redirectLocation.pathname + redirectLocation.search); } if (!renderProps) { return next(); } // Create the redux store. const store = createStore(); // Retrieve the promises from React Router components that have a fetchData method. // We use this data to populate our store for server side rendering. const fetchedData = renderProps.components .filter(component => component.fetchData) .map(component => component.fetchData(store, renderProps.params)); // Wait until ALL promises are successful before rendering. Promise.all(fetchedData) .then(() => { const asset = { javascript: { main: '/js/bundle.js' } }; const appContent = renderToString( <Provider store={store}> <RouterContext {...renderProps} /> </Provider> ) const isProd = process.env.NODE_ENV !== 'production' ? false : true; res.send('<!doctype html>' + renderToStaticMarkup(<Html assets={asset} content={appContent} store={store} isProd={isProd} />)); }) .catch((err) => { // TODO: Perform better error logging. console.log(err); }); }); }); 

Redditcontainer.js

 class Reddit extends Component { // Used by the server, ONLY, to fetch data static fetchData(store) { const { selectedSubreddit } = store.getState(); return store.dispatch(fetchPosts(selectedSubreddit)); } // This will be called once on the client componentDidMount() { const { dispatch, selectedSubreddit } = this.props; dispatch(fetchPostsIfNeeded(selectedSubreddit)); } ... Other methods }; 

HTML.js

 'use strict'; import React, { Component, PropTypes } from 'react'; import ReactDom from 'react-dom'; import Helmet from 'react-helmet'; import serialize from 'serialize-javascript'; export default class Layout extends Component { static propTypes = { assets: PropTypes.object, content: PropTypes.string, store: PropTypes.object, isProd: PropTypes.bool } render () { const { assets, content, store, isProd } = this.props; const head = Helmet.rewind(); const attrs = head.htmlAttributes.toComponent(); return ( <html {...attrs}> <head> {head.base.toComponent()} {head.title.toComponent()} {head.meta.toComponent()} {head.link.toComponent()} {head.script.toComponent()} <link rel='shortcut icon' href='/favicon.ico' /> <meta name='viewport' content='width=device-width, initial-scale=1' /> </head> <body> <div id='content' dangerouslySetInnerHTML={{__html: content}} /> <script dangerouslySetInnerHTML={{__html: `window.__app_data=${serialize(store.getState())}; window.__isProduction=${isProd}`}} charSet='utf-8' /> <script src={assets.javascript.main} charSet='utf-8' /> </body> </html> ); } }; 

Repeat ...

  • On the client, take the state variable and pass it to your store.
  • On the server, scroll through your components by calling fetchData and passing your store. Wait for promises to resolve, then render.
  • In HTML.js (your renderView function) serialize the Redux repository and render the output for the javascript variable for the client.
  • In the React component, create a static fetchData method for the ONLY server to invoke. Submit the necessary actions.
+5
source share

You can use CanUseDOM from the fbjs module.

 import { canUseDOM } from 'fbjs/lib/ExecutionEnvironment'; //only render on the server because it doesn't have DOM if(!canUseDOM) static fetch here 
+1
source share

The best idea is to dehydrate the state of your storage on the server side and hydrate the initial state of the storage on the client side with a dehydrated state.

From the Redux Doc:

This simplifies the creation of universal applications, since the state of your server can be serialized and hydrated in the client without additional coding efforts.

http://redux.js.org/docs/introduction/ThreePrinciples.html

0
source share

All Articles