Skip to content
This repository has been archived by the owner on Oct 26, 2018. It is now read-only.

Server-side render flow : is this a good approach? #251

Closed
edygar opened this issue Feb 1, 2016 · 8 comments
Closed

Server-side render flow : is this a good approach? #251

edygar opened this issue Feb 1, 2016 · 8 comments

Comments

@edygar
Copy link

edygar commented Feb 1, 2016

As I didn't find anyone using react-router-redux as the following, I was wondering, is this a good approach? I just wanted be able to load component's data and to allow my middlewares to push and replace Locations.

app.use((req, res) => {
  if (isDevelopment) {
    // Do not cache webpack stats: the script file would change since
    // hot module replacement is enabled in the development env
    webpackIsomorphicTools.refresh();
  }

  const history = createMemoryHistory();
  const store = createStore(history, {});

  history.push(req.originalUrl);

  history.listen( location => {
    match({ routes, location }, (err, redirectLocation, renderProps) => {
      if (err) {
        error('Error during routing match\n', pretty.render(err));

        res.status(500).send(render(store, renderProps));
      } else if (redirectLocation) {
        res.redirect(302, redirectLocation.pathname + redirectLocation.search)
      } else {
        store.dispatch(loadDataForComponents(renderProps))
        .then(() => {
          const status = renderProps.routes
          .reduce((prev, cur) => cur.status || prev, null);

          res.status(status || 200).send(render(store, renderProps));
        },
        err2 => {
          error('Error during transition occurred\n', pretty.render(err2));
          if (renderProps.location === store.getState().routing.location) {
            store.dispatch( routeActions.replace("/500") );
          }
        });
      }
    });
  });
});

As I understand, match is only going to bring the matching routes, but won't keep with all the redirects, and mainly, won't trigger again my action loadDataForComponents, so I needed to listen history, which is supposed to be garbage-collected after the .send call

Suppose the user session has expired and it ends in a 401 request, instead simply returning the error, I want to be able to redirect to login, fetching any required data.

What do you guys think?

@Dattaya
Copy link
Contributor

Dattaya commented Feb 1, 2016

won't trigger again my action loadDataForComponents

It will every time someone loads the page directly from the server, usually it's an initial load or for browsers that do not support browserHistory(if that's what you're using on the client)--every time.
The redirect res.redirect(302 will cause the browser to load another page and it will make another request to the server.

Suppose the user session has expired and it ends in a 401 request, instead simply returning the error, I want to be able to redirect to login, fetching any required data.

react-redux-universal-hot-example handles it in an onEnter hook if I understand your question correctly.

@edygar
Copy link
Author

edygar commented Feb 1, 2016

@Dattaya, I got the part of the redirect, indeed you're right.

Now, Imagine I want to react to a 401 response of my data api, I feel as if it was better placed as a middleware instead of a react-router hook, the later solution requires injection of the store into the hooks methods. For the primer solution, I need listen callback to be runned again so then it will redirect as you explained.

@Dattaya
Copy link
Contributor

Dattaya commented Feb 1, 2016

That's an interesting approach that I'll experiment with when I have free time. What about client-side, it should be able to handle errors from api too. Are you using match there or Router?

@edygar
Copy link
Author

edygar commented Feb 1, 2016

const history = browserHistory;
const store = createStore( history, global.__INITIAL_STATE__ );

store.dispatch(unserialize());
history.listen( location =>
  match({ routes, location }, (error, redirectLocation, renderProps)=> {
    document.body.scrollTop = 0;
    store.dispatch(loadDataForComponents(renderProps))
  })
);

info('First client render');
ReactDOM.render(
  <Provider store={store}>
    <Router history={history} routes={routes}/>
  </Provider>,
  mountNode
);

@Dattaya
Copy link
Contributor

Dattaya commented Feb 1, 2016

Both Router and match use transitionManager that's why I thought they serve about the same purpose so this setup might cause some problems if that is the case. I think just Route + its render property is enough; like it's implemented in redux-async-connect.

@edygar
Copy link
Author

edygar commented Feb 2, 2016

Is onEnter called on every Route's changes, including nested Routes, or just that specific, because I could use that hook instead history.listen + match

@timdorr
Copy link
Member

timdorr commented Feb 2, 2016

It's called whenever a route is entered. If you jump between two child nodes, that's not considering an enter event and it's not called. But if you jump to some sibling, that would be an enter event.

@edygar
Copy link
Author

edygar commented Feb 2, 2016

Thank you guys! You just helped me to improve here. What do you think about this client-side render method?

const history = browserHistory;
const store = createStore( history, global.__INITIAL_STATE__ );

store.dispatch(unserialize());
info('First client render');
ReactDOM.render(
  <Provider store={store}>
    <Router history={history} routes={routes} render={props => {
      store.dispatch(loadDataForComponents(props));
      document.body.scrollTop = 0;
      return <RouterContext {...props} />
    }}/>
  </Provider>,
  mountNode
);

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants