Updating React Context inside child components
If you want to update context from inside a child component, you can use one of the following methods. I've split this guide into two parts: one for React hooks, and one for classes.
If you're using context via React hooks
React Hooks provide a clean and simple approach to context. The end result will allow you to use an API like:
function YourComponent() {
const { state, update } = useContext(ExampleContext)
// `state` is an object containing all your context variables.
console.log(state) // { foo: "bar", baz: "qux" }
// `update` is a function that lets you update the variables; for instance:
update({ foo: "garply" })
}
In order to do this, you'll want to create a new file to hold your context. I like to put this in a high-level folder (usually directly under src
) so it's easy to import.
Create a new file for your context; for instance, ExampleContext.js. Set it up like this:
import React, { createContext, useReducer } from 'react'
export const ExampleContext = createContext()
const reducer = (state, pair) => ({ ...state, ...pair })
const initialState = {
sound: "bark"
}
export function ExampleProvider(props) {
const [state, update] = useReducer(reducer, initialState)
return (
<ExampleContext.Provider value={{ state, update }}>
{props.children}
</ExampleContext.Provider>
)
}
Now, you'll want to wrap one of your higher-level components (such as App.js) with your provider component. (I'll give an example of this in a moment.) The provider's job is to quite literally provide context to any child component. This means that only its child elements will have access to this context:
<App> # ❌ can't access context
<SomeParent> # ❌ can't access context
<ExampleProvider>
<SomeChild> # ✅ can access context
<SomeGrandchild> # ✅ can access context
<div> # ✅ can access context
<SomeGreatGrandchild> # ✅ can access context
When one of your variables in context changes, every child component will re-render with the new data, so you'll want to put this at an appropriate level. (In the example above, if only SomeGreatGrandchild
needed to have access to context, you would want to move ExampleProvider
closer to it to prevent unnecessary re-renders.)
Anyway, I promised you an example, so here it is. Let's say that you wanted your entire app to have access to the context (which is pretty common in smaller apps). You could wrap your App
component in the provider:
import { ExampleProvider } from './context/ExampleContext'
function App() {
return (
<ExampleProvider>
...the rest of your app...
</ExampleProvider>
)
}
Now, you can import ExampleContext
and destructure the state
and update
variables from it. They can be used like this:
import { ExampleContext } from './context/ExampleContext'
function YourComponent() {
const { state, update } = useContext(ExampleContext)
console.log(state.sound) // "bark"
const handleClick = () => update({ sound: "meow" })
return (
<button onClick={handleClick}>
Click to change sound to meow
</button>
)
}
And there you have it! Hopefully this was able to help you a bit.
If you're using context via React classes
If you're using context via classes, the process is somewhat similar.
-
Add a method called
updateState
to your provider, which callsthis.setState
. Then, attach it to your state:// DataProvider.jsx import React, { Component } from 'react' import DataContext from 'path/to/your/DataContext' class DataProvider extends Component { constructor(props) { super(props) this.updateState = this.updateState.bind(this) // ← Here this.state = { foobar: '...' update: this.updateState // ← Here } } updateState(values) { // ← And here this.setState(values) } render() { return ( <DataContext.Provider value={this.state}> {this.props.children} </DataContext.Provider> ) } } export default DataProvider
-
Now, inside the child component, you can use
this.context.update
to update the state. It works exactly likethis.setState
, so you can pass it a key: value pair.// Grandchild.jsx import React, { Component } from 'react' import DataContext from 'path/to/your/DataContext' class Grandchild extends Component { componentDidMount() { this.context.update({ foobar: 'It worked!' }) // ← Here } render() { return <div>{this.context.foobar}</div> } } Grandchild.contextType = DataContext export default Grandchild
That's all there is to it: you've updated a React component's state from inside a child component via Context. If you need more guidance, I recommend reading my guide on setting up React Context.