Back to all notes
Context Selector : Subscribe to Just What You Need
Use use-context-selector to avoid re-renders by selecting slices of context.
Introduction
Context selectors let components subscribe to only part of a context value. Instead of re-rendering whenever any field changes, you pick exactly what you care about.
Why this matters
Regular useContext re-renders consumers whenever any part of the value changes. With useContextSelector, a component re-renders only when the selected slice changes.
The problem
Consumers that read the entire context object will re-render on every change—even if they only need one field.
Inefficient approach
Using useContext causes unnecessary renders:
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;
The solution
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;
Step-by-step
- Replace
useContextwithuseContextSelector. - Provide a selector
(v) => v.nameor(v) => v.countper consumer. - Ensure the provider value shape is stable (memoize complex objects if needed).
- Verify that unrelated context updates no longer re-render the component.
Tips
- Keep the provider value stable (e.g., memoize with
useMemo) when it grows complex. - Prefer selectors that return primitives or stable references to reduce churn.
- Measure before optimizing—reach for selectors where re-renders are actually costly.
Knowledge base
Problem snippet:
const { name } = useContext(AppContext);
Solution snippet:
const name = useContextSelector(AppContext, (v) => v.name);