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.

2 min read

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

  1. Replace useContext with useContextSelector.
  2. Provide a selector (v) => v.name or (v) => v.count per consumer.
  3. Ensure the provider value shape is stable (memoize complex objects if needed).
  4. 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);