Simple and reusable React Context API example (HOC)

Niwat Charoenloet
Sep 5, 2018 · 3 min read

This is an example of how to use the new React Context API in a modular and concise way, using an HOC.

The example shows how to propagate a theme color across a minimal React app. Demo and sandbox: https://codesandbox.io/s/881np4xqp9

One of the advantages of using react Context is to avoid prop-drilling (passing props down a chain of component), in order to re-render of a set of components. This example is minimal but this pattern works equally with nested components (which is the goal).

Let’s say we have 3 components:

App.js
SetColor.js
Menu.js

Menu.js is a component that will change color depending on the theme:

import * as React from "react";
import { withTheme } from "./ThemeContext/withTheme";
const Menu = props => (
<div style={{ backgroundColor: props.themeContext.color }}>
Menu
</div>
);
export default withTheme(Menu);
  • In 2 lines with code, we can inject a theme color in our Menu component, and read this theme color string from the props themeContext

SetColor.js is component to set the color of the theme:

import * as React from "react";
import { withTheme } from "./ThemeContext/withTheme";
const SetColor = props => (
<div>
<button onClick={() => props.themeContext.setColor("red")}>Red
</button>
<button onClick={() => props.themeContext.setColor("blue")}>Blue
</button>
</div>
);
export default withTheme(SetColor);
  • We inject a themeContext props in our SetColor component, and set the theme color from using themeContext.setColor()

If we click on a button, it will propagate the color changes to our Menu, blue or red values here.


The achieve this, we only need 3 files, which we will organize under a folder ThemeContext.

App.js
SetColor.js
Menu.js
ThemeContext/
- theme-context.js
- withTheme.js
- ThemeProvider.js

theme-context.js

import * as React from "react";export const ThemeContext = React.createContext(
// default values used by a Consumer when it does not have a
// matching Provider above it in the tree, useful for testing
{
themeContext: {
color: 'blue',
setColor: () => {}
}
}
)

withTheme.js

import * as React from 'react';
import {ThemeContext} from './theme-context';
export function withTheme(Component) {
return function ThemeComponent(props) {
return (
<ThemeContext.Consumer>
{(contexts) => <Component {...props} {...contexts} />
}
</ThemeContext.Consumer>
)
}
}
  • withTheme.js is our High-Order-Component (HOC), which let us use export withTheme(AnyComponent) to inject our theme context (themeContext props) in any component.

ThemeProvider.js

import * as React from 'react';
import {ThemeContext} from './theme-context';
export default class ThemeProvider extends React.Component {constructor() {
super();
this.state = {
setColor: this.setColor.bind(this),
color: "yellow"
};
}
setColor(color) {
this.setState({color});
}
render() {
return (
<ThemeContext.Provider value={{
themeContext: {
...this.state
}
}}>
{this.props.children}
</ThemeContext.Provider>
)
}
}
  • ThemeProvider.js is our context component containing our theme color state, and the setColor function to change it.
  • We are passing thecolor string and the setColor function in a wrapper object themeContext which will be the unique props injected by thewithTheme HOC.
    We could have passed color and setColor as two props directly without nesting them in a themeContext object, but I think this indicates clearly what is being injected by withTheme to a component. This also works well with Flow props typing.

App.js — Finally, let’s not forget to modify App.js:

import * as React from "react";
import ThemeProvider from "./ThemeContext/ThemeProvider";
import Menu from "./Menu";
import SetColor from "./SetColor";
export default () => (
<ThemeProvider>
<Menu />
<br />
<SetColor />
</ThemeProvider>
);
  • ThemeProvider needs to be above any child component that will consume our theme context. In the example above, the children components are right under it, but they could have been as well nested under 50 sub-components.

Demo and sandbox : https://codesandbox.io/s/881np4xqp9

That’s it! Let me know if that was helpful.

Niwat Charoenloet

Written by

Love designing and building products. Founded MonsieurBarbier.com, Tokenpicker.com, Moonbar.org. Currently SW eng. at Volley.com

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade