React Context
🌱 This post is in the growth phase. It may still be useful as it grows up.
Contents
- A “Shit” Example
- Create, Consume, and Provide Context
- Provide
value - Authoring and Modules
- Cascading Context
- Data Distribution, Not State Management
- A Mental Model for Context
useContextHookstatic contextTypeHook
A “Shit” Example
This is a shit example of Context. Shit because it uses “shit” as an illustration and because it’s simplistic.
We’ll get to the why after we cover the how.
”Shit” is a fine word
“Shit” is fine word in my house.
But my mom hates it.
I tell my kids: “when your at grandma’s house, use a different word.”
Let’s implement this scenario using React Context.
// 1. It's ok to say "shit" as a default.const ExpletiveContext = React.createContext('shit')
// 2. But use a word that's contextually appropriate.function ContextualExclamation() { let word = React.useContext(ExpletiveContext)
return <>Oh {word}!</>}
// 3. At grandma's house, use the word "snap" insteadfunction GrandmasHouse(props) { return ( <ExpletiveContext.Provider value="snap"> {props.children} </ExpletiveContext.Provider> )}
// 4. Something exciting happened. What do you say?function VisitToGrandmasHouse() { return ( <GrandmasHouse> <ContextualExclamation /> </GrandmasHouse> )}
// => Oh snap!Prefer video?
Create, Consume, and Provide Context
Context is a 3-part system: create, use, provide.
Create
Context must first be defined.
Create context using React.createContext.
let NameContext = React.createContext()Create (with default value)
React.createContext takes take an optional. Define it using a sensible fallback
let NameContext = React.createContext('Guest')Prefer video?
Use
Context is accessed with the React.useContext hook.
Consumer
Provided functions get the Context’s value as their first argument.
let NameContext = React.createContext("Guest");
function ContextGreeting() { let value = React.useContext(NameContext);
return <h1>👋 {value}!</h1>} }
// => <h1>👋 Guest!</h1>In this example, value is the default value used to create NameContext.
So, how do we provide Context? I’m always glad you ask…
Prefer video? Watch along at learnreact.com.
Provide
Provider is a component that takes a value prop and makes it available to every component in the component tree below it.
let NameContext = React.createContext('Guest')
let ContextGreeting = () => ( <NameContext.Provider value="Michael"> <NameContext.Consumer> {(name) => <h1>👋 {name}!</h1>} </NameContext.Consumer> </NameContext.Provider>)
// => <h1>👋 Michael!</h1>Providers work where components are deeply nested.
let NameContext = React.createContext('Guest')
let ContextAwareName = () => ( <NameContext.Consumer> {(name) => <h1>👋 {name}!</h1>} </NameContext.Consumer>)
let NestedContextAwareName = () => <ContextAwareName />
let DeeplyNestedContextAwareName = () => ( <NestedContextAwareName />)
let ContextGreeting = () => ( <NameContext.Provider value="No Prop Drills"> <DeeplyNestedContextAwareName /> </NameContext.Provider>)
// => <h1> Welcome No Prop Drills!</h1>Prop Drills not required for assembly.
Prefer video? Watch along at learnreact.com.
Provide value
A Context’s value can take any shape.
Here are examples of valid Contexts values, using a default value:
let StringContext = React.createContext('string')
let NumberContext = React.createContext(42)
let FunctionContext = React.createContext(() => alert('Context function'))
let ArrayContext = React.createContext([ 'some', 'array', 'elements',])
let ObjectContext = React.createContext({ aString: 'string', aNumber: 42, aFunction: () => alert('Context function'), anArray: ['some', 'array', 'elements'],})value can be complex structures like React Elements, class components, and function components.
let ReactElementContext = React.createContext( <span>React Element</span>)
let FunctionalComponentContext = React.createContext( (props) => <span>Function Component</span>)
let ClassComponentContext = React.createContext( class extends React.Component { render() { return <span>Class Component</span> } })value is required on Context Providers
Where a Context Provider is used, the value prop is required.
// NOPE<SomeContext.Provider></SomeContext.Provider>
// YEP!<SomeContext.Provider value="value is a required prop"></SomeContext.Provider>What about the default value given to createContext?
The default value given to createContext is used for Consumer components without a Provider.
Where Providers wrap their Consumers, all bets are off.
You must explicitly provide a value.
let UserContext = React.createContext('Guest')
let ContextGreeting = () => ( <UserContext.Consumer> {(word) => <span>Hi {word}!</span>} </UserContext.Consumer>)
let App = (props) => ( <div> <ContextGreeting /> {/* => Hi Guest! */} <UserContext.Provider> <ContextGreeting /> {/* => Hi ! */} </UserContext.Provider> <UserContext.Provider value="Bulbasaur"> <ContextGreeting /> {/* => Hi Bulbasaur! */} </UserContext.Provider> </div>)Prefer video? Watch along at learnreact.com.
Authoring and Modules
A Context’s Consumer and Provider components can be accessed in 2 ways.
The Examples above use JSX’ property access syntax. This is the style used in official documentation.
<SomeContext.Provider value="some value"> <Context.Consumer> {(value) => <span>{value}</span>} </Context.Consumer></SomeContext.Provider>Above, you access the Provider and Consumer components through the Context object.
You may prefer to use object destructuring to assign Provider and Consumer components to local variables.
// Destructure your Context's Consumer and Providerlet {Consumer, Provider} = SomeContext
;<Provider value="some value"> <Consumer>{(value) => <span>{value}</span>}</Consumer></Provider>Take care where multiple contexts are used.
let { Consumer: OrganizationConsumer, Provider: OrganizationProvider,} = React.createContext()
let {Consumer: PersonConsumer, Provider: PersonProvider} = React.createContext()
let App = () => ( <OrganizationProvider value="ACME Co."> <PersonProvider value="Yakko"> <OrganizationConsumer> {(organization) => ( <PersonConsumer> {(person) => ( <span> {person}, {organization} </span> )} </PersonConsumer> )} </OrganizationConsumer> </PersonProvider> </OrganizationProvider>)
// => Yakko, ACME Co.Cascading Context
Context cascades.
Consumers use the value from the nearest Context.Provider.
Where none is present, the createContext default value is used.
let {Provider, Consumer} = React.createContext('global default')
function App() { return ( <> <Provider value="outer"> <Consumer> {(value) => <div>{value}</div> /* "outer" */} </Consumer>
<Provider value="inner"> <Consumer> {(value) => <div>{value}</div> /* "inner" */} </Consumer> </Provider> </Provider>
<Consumer> {(value) => <div>{value}</div> /* "global default" */} </Consumer> </> )}Data Distribution, Not State Management
Context makes it possible to distribute data to every component in a component tree.
It’s used to distribute data, not manage state. That said, it provides the mechanism needed to state and updater functions managed by state containers.
Here’s an example of a stateful container that uses Context to distribute local state and an update function.
let StateContext = React.createContext()
class StateProvider extends React.Component { static defaultProps = { initialState: {}, }
update = (updater, done) => { this.setState( (prevState) => ({ state: typeof updater === 'function' ? updater(prevState.state) : updater, }), done ) }
state = { state: this.props.initialState, update: this.update, }
render() { return ( <StateContext.Provider value={this.state}> {this.props.children} </StateContext.Provider> ) }}
let App = () => ( <StateProvider initialState={{count: 0}}> <StateContext.Consumer> {({state, update}) => ( <div> <div>{state.count}</div>
<button type="button" onClick={() => update(({count}) => ({count: count + 1})) } > increment </button> </div> )} </StateContext.Consumer> </StateProvider>)Modularazing Context
In the “real world”, you’ll likely expose Contexts via ES Modules.
import React from 'react'
let {Provider, Consumer} = React.createContext()
export {Provider, Consumer}import React from 'react'
let {Provider, Consumer} = React.createContext()
export {Provider, Consumer}Consumers can be imported to compose context-aware components.
import React from 'react'import {Consumer as PersonConsumer} from './person_context'import {Consumer as OrganizationConsumer} from './organization_context'
export function ContextBizCard() { return ( <OrganizationConsumer> {(organization) => ( <PersonConsumer> {(person) => ( <div className="business-card"> <h1>{person}</h1> <h3>{organization}</h3> </div> )} </PersonConsumer> )} </OrganizationConsumer> )}Providers can be imported to contain and supply values to context-aware components.
import {Provider as OrganizationProvider} from './organization_context'import {Provider as PersonProvider} from './person_context'import {ContextBizCard} from './context_biz_card'
let App = () => ( <OrganizationProvider value="ACME Co."> <PersonProvider value="Yakko"> <ContextBizCard /> </PersonProvider> </OrganizationProvider>)
// => Yakko, ACME Co.A Mental Model for Context
Props are like wires. Props are used to “connect” data between components. Like wires, the components have to be “touching”. Meaning that component thats hold data have to render components that need it.
Context is like a wireless — like infrared.
Context is used to send a “signal”.
Like wireless, components have to be “in range” — children of a Context.Provider — to recieve the signal.
Components can observe that signal with a Context.Consumer.
useContext Hook
As of v16.8 React provides a Hooks API for consuming context.
The main difference between the Context.Consumer component and the useContext Hook is this:
The useContext Hook requires a component boundary;
Context.Consumer can be used inline.
Here’s a re-implementation of A “Shit” Example using hooks:
import React from 'react'
const ExpletiveContext = React.createContext('shit')
function ContextualExclamation() { let word = React.useContext(ExpletiveContext)
return ( <ExpletiveContext.Consumer> {word} </ExpletiveContext.Consumer> )}
function VisitGrandmasHouse() { return ( <ExpletiveContext.Provider value="poop"> <ContextualExclamation /> </ExpletiveContext.Provider> )}static contextType Hook
Class components can consume one Context directly using static contextType.
This differs from Context.Consumer in that it happens in the component definition and it can only consume one Context.
Access to context is done thru the component instance.
Here’s a re-implementation of A “Shit” Example using static contextType:
import React from 'react'
const ExpletiveContext = React.createContext('shit')
class ContextualExclamation extends React.Component { static contextType = ExpletiveContext
render() { return ( <ExpletiveContext.Consumer> {this.context.word} </ExpletiveContext.Consumer> ) }}
function VisitGrandmasHouse() { return ( <ExpletiveContext.Provider value="poop"> <ContextualExclamation /> </ExpletiveContext.Provider> )}© 2018 Michael Chan Some Rights Reserved

This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.