πŸͺ† Nesting and overriding new React Context API

Ever wondered how react-router lets a nested route see its own match instead of its parent’s? I went digging through its source to find out, and the trick turned out to be something you can use in your own components with the new React Context API.

While learning react-router v4 I read some of their source code. And as we know they are using current context for passing down router and route info overriding previous/parent route info

{% github https://github.com/ReactTraining/react-router %}

getChildContext() {
  return {
    router: {
      ...this.context.router,
      route: {
        location: this.props.location || this.context.router.route.location,
        match: this.state.match
      }
    }
  };
}

React team announced new Context API that no longer will be deprecated in React v16.3.0, that is already released :)

https://github.com/facebook/react/releases/tag/v16.3.0

Now I was thinking how ReactTraining will make this overriding using new Context API. From start I used create-react-context polyfill for new context. It works exactly, just change the import.

import { render } from "react-dom";
import React, { createContext } from "react";
// import createContext from "create-react-context";

Next we need to create the context. Context has a Consumer and a Provider

const { Provider, Consumer } = createContext();

Provider is used to pass to him some data in value prop

render() {
    return (
        <Provider value={"React is Awesome!"}>
            nested content...
        </Provider>
    )
}

And Consumer is used to consume that value using render props

render() {
    return (
        <Consumer>
            {(theValue) => {
                return theValue
            }}
        </Consumer>

        // shorthand
        <Consumer>
            {theValue => theValue}
        </Consumer>
    )
}

// output
// React is Awesome!

We may use the Consumer how many times we want.

Now back to our overriding. Here is my app

const App = () => (
	<Provider value={{ location: "/" }}>
		<NestedPath>
			<NestedPath location="haha/">
				<NestedPath>
					<NestedPath>
						<NestedPath>
							<NestedPath />
						</NestedPath>
					</NestedPath>
				</NestedPath>
			</NestedPath>
		</NestedPath>
	</Provider>
);

ReactDOM.render(<App />, document.getElementById("root"));

And here is the output

/
/location/
/location/haha/
/location/haha/location/
/location/haha/location/location/
/location/haha/location/location/location/

And this is my NestedPath component

const NestedPath = ({ location = "location/", children }) => (
	<Consumer>
		{(router) => (
			<React.Fragment>
				<div>{router.location}</div>
				<Provider
					value={{ ...router, location: router.location + location }}
				>
					{children || null}
				</Provider>
			</React.Fragment>
		)}
	</Consumer>
);

Here as you see inside Provider we override previous one with a new value. And all child Consumers now will take the new value.

Here is a sandbox to play with

https://codesandbox.io/s/lrvv8w784q

Thanks for reading!!! This is a duplicate of my Medium Story! dev.to is new medium for developers :)

Created At
7/6/2018
Updated At
9/15/2023
Published At
7/6/2018