How to use context api with react router v4?

It looks like that <Switch> should only have <Route> and <Redirect > components as direct children. (source)

I suppose that's why your Redirect doesn't work as you use ContextB as a Switch child.

The simplest but repetitive solution could be to pass your ContextB as a child of each <Route> you want:

Note: These solutions suppose that you assigned the default value of your Context component like this: const MyContext = React.createContext(defaultValue);

<Route exact path='/route2'>
  <ContextB.Provider>
    <Component1 />
  </ContextB.Provider>
</Route>

You can even create a ContextRoute component for this:

import React from 'react';
import { Route } from 'react-router-dom';

const ContextRoute = ({ contextComponent, component, ...rest }) => {
  const { Provider } = contextComponent;
  const Component = component;

  return (
    <Route {...rest}>
      <Provider>
        <Component />
      </Provider>
    </Route>
  );
};

export default ContextRoute;

And then use it as a Route:

<ContextA>
  <Switch>
    <Route exact path='/route1' component={ Component1 } />
    <ContextRoute exact path='/route2' contextComponent={ContextB} component={ Component2 } />
    <ContextRoute exact path='/route3' contextComponent={ContextB} component={ Component3 } />
    <Redirect from='/' to='/route1' />
  </Switch>
</ContextA>

With this solution, you then use your context with render props in your nested Components:

return (
  <ContextB.Consumer>
    {value => <div>{value}</div>}
  </ContextB.Consumer>
);

But we can imagine much more solutions to this like HOC, passing context value directly to the route component props, etc...


As a warning to others, accepted answer doesn't really work as you would expect from the original (non-working) concept:

// This comes from the original question (doesn't work as-is!)
<ContextA>
  <Switch>
    <Route exact path='/route1' component={ Component1 } />
      <ContextB>
        <Route exact path='/route2' component={ Component2 } />
        <Route exact path='/route3' component={ Component3 } />
      </ContextB>
      <Redirect from='/' to='/route1' />
   </Switch>
</ContextA>

In there, /route2 and /route3 are sharing a context and the expectation should be that:

  1. State is kept between routes transitions.
  2. If either Component2 or Component3 update the context, changes should be reflected back to the other.

None of the above is true for the accepted solution.