Context Selector : Subscribe to Just What You Need
Use use-context-selector to avoid re-renders by selecting slices of context.
Plain useContext has a blunt contract: change any field of the value and every consumer re-renders. Most of my components read exactly one field. A context selector lets a component subscribe to just that slice, so it re-renders only when the selected slice changes.
One field changes, every consumer re-renders
Two components under one provider. NameComponent reads name, CountComponent reads count:
import { createContext, useContext, useState } from "react";
const AppContext = createContext();
function NameComponent() {
const { name } = useContext(AppContext);
return <p>Name: {name}</p>;
}
function CountComponent() {
const { count } = useContext(AppContext);
return <p>Count: {count}</p>;
}
function App() {
const [name, setName] = useState("John");
const [count, setCount] = useState(0);
return (
<div>
<h1>Context Selector</h1>
<AppContext.Provider value={{ name, count }}>
<NameComponent />
<CountComponent />
<button onClick={() => setCount(count + 1)}>Increment</button>
</AppContext.Provider>
</div>
);
}
export default App;
Both get the whole context object from useContext. Press Increment and NameComponent re-renders too, even though it only ever needed name.
A selector per component
Select name and count independently:
import { createContext, useContext, useState, useMemo } from "react";
const AppContext = createContext();
// Simplified useContextSelector implementation
function useContextSelector(context, selector) {
const value = useContext(context);
return useMemo(() => selector(value), [value, selector]);
}
function NameComponent() {
const name = useContextSelector(AppContext, (v) => v.name);
return <p>Name: {name}</p>;
}
function CountComponent() {
const count = useContextSelector(AppContext, (v) => v.count);
return <p>Count: {count}</p>;
}
function App() {
const [name, setName] = useState("John");
const [count, setCount] = useState(0);
const contextValue = useMemo(() => ({ name, count }), [name, count]);
return (
<div>
<h1>Context Selector</h1>
<AppContext.Provider value={contextValue}>
<NameComponent />
<CountComponent />
<button onClick={() => setCount(count + 1)}>Increment</button>
</AppContext.Provider>
</div>
);
}
export default App;
Each consumer now picks its field with a selector like (v) => v.name. The rest is hygiene: keep the provider value stable, memoizing complex objects, then verify that unrelated updates actually stopped re-rendering the component.
Selectors that return primitives, or references that stay stable between renders, cause the least churn. And measure before reaching for this. It’s worth it where re-renders are actually costly.