Shared State & Context
In React, state is typically local to a component. However, there are cases where you want to share state across multiple components without passing props down through every level of the component tree. This is where React's Context API comes in handy.
1. Create the Context
// UserContext.tsx
import {
createContext,
useContext,
useState,
ReactNode,
} from 'react'
interface UserContextType {
name: string
setName: (name: string) => void
}
const UserContext = createContext<UserContextType | undefined>(undefined)
interface UserProviderProps {
children: ReactNode
}
export const UserProvider = ({
children,
}: UserProviderProps) => {
const [name, setName] = useState('René')
return (
<UserContext.Provider
value={{
name,
setName,
}}
>
{children}
</UserContext.Provider>
)
}
export const useUser = () => {
const context = useContext(UserContext)
if (!context) {
throw new Error('useUser must be used inside UserProvider')
}
return context
}
2. Wrap Your App
// App.tsx
import { UserProvider } from './UserContext'
import UserDisplay from './UserDisplay'
import UserInput from './UserInput'
const App = () => (
<UserProvider>
<UserDisplay />
<UserInput />
</UserProvider>
)
export default App
3. Read State
// UserDisplay.tsx
import { useUser } from './UserContext'
const UserDisplay = () => {
const { name } = useUser()
return <h2>Hello {name}</h2>
}
export default UserDisplay
4. Update State
// UserInput.tsx
import { useUser } from './UserContext'
const UserInput = () => {
const { name, setName } = useUser()
return (
<input
type="text"
value={name}
onChange={(event) =>
setName(event.target.value)
}
/>
)
}
export default UserInput
Result
When the user types in the input:
[ René ]
the value is updated in the Context and automatically reflected everywhere that uses:
const { name } = useUser()
This removes the need to pass props through multiple component levels ("prop drilling") and is often the first step toward application-wide state management in React.