React-router: Q: Allow to use `createStaticHistory` for `ConnectedStaticRouter`

Created on 22 Dec 2017  Â·  2Comments  Â·  Source: ReactTraining/react-router

Version: 4.1.1

The problem

I'm using ConnectedRouter on client and want to have isomorphic access to location and control over it.

The StaticRouter is really great!

And he keeps isomorphic code working over environments!

  • <StaticRouter keeps history freezed and unchanged (this is very important for server-side rendering)
  • Keeps components like <Redirect working
  • Indicates possible requirement of redirects via context.url
    (because server render tried to change static history, but we can change it only while handling new request)

Going deeper in application data-flow, depending on location

If we need to work with location from redux-store (e.g. is redux-saga) on server - there is no location in store
Also if we dispatch and push or redirect action - they will not be handled, because StaticRouter history cannot be provided into routerMiddleware

Philosophy of code architecture

I reviewed comment from @timdorr in #5475.
As i understand, he recommends to write our own actions to handle push/replace on server
But I do not agree with this comment.
We already have history and routing via Router and ConnectedRouter
So why we need to write different code for client-side and for server-side, if this code do the same things?
The only difference between client and server code is how it will be handled on top level of application in different environments
(e.g. window.history.replaceState vs response.redirect)

Proposition to improve server-side rendering support

I think this problems can be solved by writing modified ConnectedStaticRouter

This is how it may looks on server:

// staticHistory currently implemented inside `StaticRouter` and must be extracted 
// https://git.io/vb97k
import createHistory from 'history/createStaticHistory'

// Some express.js middleware
function renderReactPage(req, res) {
  const routerContext = {}
  // static router context need to be provided into staticHistory (but now is hardcored inside StaticRouter)
  const history = createHistory(routerContext)

  // `react-router-redux` reducer and middleware declaration still same as currently implemented
  const middleware = routerMiddleware(history)
  const store = createStore(
    combineReducers({ router: routerReducer }),
    applyMiddleware(middleware)
  )

  const html = renderToString(
    <Provider store={store}>
      <ConnectedStaticRouter location={url} context={routerContext} staticHistory={history}>
        <App />
      </ConnectedStaticRouter>
    </Provider>
  )

  if (routerContext.url) return res.redirect(301, routerContext.url)
  res.send(html)
}

Even if routerContext is useless for ConnectedStaticRouter:

  • we still need staticHistory functionality because it guarantees that the match of some <Route can't change its match to another <Route during server render
  • location in store must stay unchanged by actions.

Relates to #5475 #5333 #4892

P.S.

I'll be happy to make PR, if we find compromise way to improve router<->redux connection for SSR

Most helpful comment

@timdorr Yep, its not so hard to dispatch LOCATION_CHANGE action to sync history state, but

  • we don't have access to static history that hard-coded in StaticRouterand we're forced to create second instance of history #4892

Also you probably forgot about <Redirect and dispatch(replace(…)) problem

  • StaticRouter does not support dispatch(replace(…))
  • ConnectedRouter on server-side-render does not support <Redirect and replace(…) and static history

This is the reason to improve SSR

All 2 comments

If we need to work with location from redux-store (e.g. is redux-saga) on server - there is no location in store

Yes, there is. Traditionally, this is available at req.url in Express and similar servers. You would initialize your history with this location already provided. You can also dispatch this into the store so the two are in sync.

You should be using <StaticRouter> for this. A syncing router is not needed, as you can't re-render on the server side so there is no ability to react to a change in the location, other than handling that outside of the React renderToString context.

@timdorr Yep, its not so hard to dispatch LOCATION_CHANGE action to sync history state, but

  • we don't have access to static history that hard-coded in StaticRouterand we're forced to create second instance of history #4892

Also you probably forgot about <Redirect and dispatch(replace(…)) problem

  • StaticRouter does not support dispatch(replace(…))
  • ConnectedRouter on server-side-render does not support <Redirect and replace(…) and static history

This is the reason to improve SSR

Was this page helpful?
0 / 5 - 0 ratings

Related issues

sarbbottam picture sarbbottam  Â·  3Comments

winkler1 picture winkler1  Â·  3Comments

wzup picture wzup  Â·  3Comments

alexyaseen picture alexyaseen  Â·  3Comments

ackvf picture ackvf  Â·  3Comments