Sometimes, multiple components need access to the same state. Lifting state up works well for nearby components, but prop drilling becomes cumbersome when components are deeply nested or far apart in the component tree.
React Context lets components share a value without passing props through every intermediate component.
A custom Hook reuses logic, not state.
function useCount() {const [count, setCount] = useState(0)return {count,increment: () => setCount(c => c + 1),}}
Using this Hook in two components creates two independent state instances:
function DisplayCount() {const { count } = useCount()return <div>{count}</div>}function IncrementCount() {const { increment } = useCount()return <button onClick={increment}>Increment</button>}
Clicking the button updates only the state owned by IncrementCount.
To share state, both components need to read from the same state owner.
Create a Context and a Provider that owns the state.
import {createContext,useContext,useState,type ReactNode,} from 'react'type CountContextValue = {count: numberincrement: () => void}const CountContext = createContext<CountContextValue | null>(null)function CountProvider({ children }: { children: ReactNode }) {const [count, setCount] = useState(0)function increment() {setCount(c => c + 1)}return (<CountContext.Provider value={{ count, increment }}>{children}</CountContext.Provider>)}function useCount() {const context = useContext(CountContext)if (context === null) {throw new Error('useCount must be used inside CountProvider')}return context}
Consume the shared state from either component:
function DisplayCount() {const { count } = useCount()return <div>Count: {count}</div>}function IncrementCount() {const { increment } = useCount()return <button onClick={increment}>Increment</button>}
Wrap both components in the same Provider:
function App() {return (<CountProvider><DisplayCount /><IncrementCount /></CountProvider>)}
Now both components access the same state. Clicking the button updates the value displayed by DisplayCount.
useContext.Consider this Provider:
<CountContext.Provider value={{ count, increment }}>{children}</CountContext.Provider>
The object { count, increment } is recreated whenever the Provider renders. Because its identity changes, Context consumers may re-render even when count has not changed.
For larger applications, you can stabilize the value with useMemo and callbacks with useCallback when profiling shows that it helps.
Another option is to split state and actions into separate Contexts, so components that only dispatch updates do not subscribe to the changing state Context.
Important: Context is a value-sharing mechanism, not a complete state-management or performance-optimization solution.