reduxreact-routerreact-router-redux

Update redux state with new route params when route changes


I am currently trying to implement a universal app and am using route params throughout my whole application. As such I want to put the route params into state.

I am able to do this ok for the SSR using the below...

router.get('/posts/:id', (req, res) => {
  res.locals.id = req.params.id

  const store = createStore(reducers, getDefaultStateFromProps(res.locals), applyMiddleware(thunk));
  const router = <Provider store={store}><StaticRouter location={req.url} context={}><App {...locals} /></StaticRouter></Provider>;      
  const html = renderToString(router);
  const helmet = Helmet.renderStatic();

  res.render('index', {
    content: html,
    context: JSON.stringify(store.getState()),
    meta: helmet.meta,
    title: helmet.title,
    link: helmet.link
  });
});

And from here the id is put into state using the getDefaultStateFromProps function... export function getDefaultStateFromProps({ id = ''} = {}) => ({ id })

This all works perfectly and puts the correct id into the redux state, which I can then use when hitting this route.

The problem I have is that when I change route on the client side, I'm not sure how to update the redux state for the id from the url.

In terms of my handling of routes I am using the following:

import React, {Component} from 'react';
import { Switch } from 'react-router-dom';
import Header from './header/';
import Footer from './footer';
import { renderRoutes } from 'react-router-config';

export default class App extends Component {
  render() {
    return (
      <div>
        <Header />
        <Switch>
          {renderRoutes(routes)}
        </Switch>
        <Footer />
      </div>
    );
  }
}

export const routes = [
  {
    path: '/',
    exact: true,
    component: Home
  },
  {
    path: '/posts/:id',
    component: Post,
  } 
  {
    path: '*',
    component: PageNotFound
  }
];

And then use the following to hydrate...

const store = createStore(reducers, preloadedState, applyMiddleware(thunk));

const renderRouter = Component => {
  ReactDOM.hydrate((
    <Provider store={store}>
      <Router>
        <Component />
      </Router>
    </Provider>
  ), document.querySelectorAll('[data-ui-role="content"]')[0]);
};

So what I'm wondering is how when I make a route change... how can I update the redux state for the new :id from the route param?

I'm a little lost in how to approach this... any help is appreciated.


Solution

  • You'll need to import routes from your route definition file.

    import { matchPath } from 'react-router';
    import { LOCATION_CHANGE } from 'react-router-redux';
    
    // LOCATION_CHANGE === '@@router/LOCATION_CHANGE';
    someReducerFunction(state, action){
      switch(action.type){
        case LOCATION_CHANGE:
          const match = matchPath(action.payload.location.pathname, routes[1]);
          const {id} = match.params;
          // ...
        default:
          return state;
      }
    }
    

    Fully working example: https://codesandbox.io/s/elegant-chaum-7cm3m?file=/src/index.js