React context normally notifies every consumer when its value changes, even if a consumer needs only one property. use-context-selector lets a component select the part it reads, so an unrelated change need not re-render that component. It is an optimization for shared context state, not a replacement for choosing an appropriate state-management approach. section 2.1.3 section 8.9
The setup has two parts:
- Create the context with createContext from use-context-selector; a context created with React’s createContext is not interchangeable here. section 2.1.3
- Read it with useContextSelector(Context, selector), or put that call behind a domain hook that accepts the selector. Select the value a consumer actually needs. section 2.1.3 section 8.4.2
A dark-mode toggle
A provider exposes isDarkMode and a toggle callback. A button that needs the current mode selects isDarkMode; the toggle component selects only the callback. Defining that callback with useCallback and a functional state update keeps it stable across mode changes. The toggle component therefore need not re-render for the changed mode, although its child button still reads the mode to choose its styling. section 2.1.3
Listing 2.3 Dark mode with context selector
import { useState, useCallback, memo } from "react";
import { #1
createContext, #1
useContextSelector, #1
} from "use-context-selector"; #1
const DarkModeContext = createContext({});
function Button({ children, ...rest }) {
const isDarkMode = #2
useDarkMode((ctx) => ctx.isDarkMode); #2
...
}
function ToggleButton() {
const toggle = useDarkMode((ctx) => ctx.toggle); #3
return <Button onClick={toggle}>Toggle mode</Button>;
}
const Header = memo(function Header() {
...
});
function Main() {
const isDarkMode = #3
useDarkMode((ctx) => ctx.isDarkMode); #3
...
}
function DarkModeProvider({ children }) {
const [isDarkMode, setDarkMode] = useState(false);
const toggle = #4
useCallback(() => setDarkMode((v) => !v), []); #4
const contextValue = { isDarkMode, toggle }; #5
return (
<DarkModeContext.Provider value={contextValue}>
{children}
</DarkModeContext.Provider>
);
}
function useDarkMode(selector) { #6
return useContextSelector( #6
DarkModeContext, #6
selector #6
); #6
}
export default function App() {
return (
<DarkModeProvider>
<Main />
</DarkModeProvider>
);
}
The dark-mode implementation places the package’s createContext and useContextSelector alongside separate selectors for state and the stable toggle action. section 2.1.3
The same pattern scales to a reducer-backed provider: a small useData(selector) hook can expose selected state or actions, while feature hooks retrieve the values they need. It can sit alongside useReducer, useReduction, and Immer; it addresses context-consumer rendering rather than reducer boilerplate or immutable updates. section 8.4 section 8.4.2
Tests still need a provider. When rendering a context-dependent component alone, wrap it in the package-created context’s Provider with the selected value or a mock callback; then assert the rendered result or interaction. React’s built-in context follows the same testing pattern, without selectors. section 10.3.3
