11/25 lessons44%

UseContextSelector and Other Smart Patterns

Sometimes, I wish React's useContext came with a big red warning label: "Will re-render all consumers. Handle with care." The first time I used it for global state—auth, theme, language settings—the re-renders were brutal. Components that had zero business updating were firing off like it was New Year’s Eve. That’s when I found useContextSelector. It’s not in React core (yet), but it should be. If you care about performance and clean component isolation, this little gem will save your bacon.

Why We Need `useContextSelector` (Context Is Too Chatty)

React's built-in useContext is like that coworker who CCs everyone on every email. Change a single key in your context object, and every component subscribed to it updates. Doesn’t matter if they only needed one little value. Boom—re-rendered.

Here's the classic trap:

javascript
1
2
3
4
5
6
7
8
9
10
11
12
          const AppContext = createContext();

function AppProvider({ children }) {
  const [theme, setTheme] = useState("dark");
  const [user, setUser] = useState({ name: "Mo" });

  return (
    <AppContext.Provider value={{ theme, user }}>
      {children}
    </AppContext.Provider>
  );
}
        

Then in your app:

javascript
1
          const user = useContext(AppContext).user;
        

Looks clean, right? But update theme and this component still re-renders. Why? Because the context value is a new object every time something changes. React isn’t selective—it just re-runs everything that touches the context.

This bit me hard in a large dashboard app. Every change in user permissions caused unrelated UI to re-render, making the whole thing feel sluggish. Enter `use-context-selector`, a small but powerful library that gives you precision reactivity.

How It Works (No Magic, Just Smart Engineering)

useContextSelector lets you subscribe to just one slice of context—nothing more.

Here’s how it looks:

javascript
1
2
3
4
5
6
7
8
9
10
11
          import {
  createContext,
  useContextSelector
} from 'use-context-selector';

const AppContext = createContext();

function Sidebar() {
  const theme = useContextSelector(AppContext, v => v.theme);
  return <div className={theme}>Sidebar</div>;
}
        

The v => v.theme bit is the magic sauce. You’re saying, “Hey, I only care about theme. Don’t bother me with anything else.” And React listens—this component won’t re-render unless theme changes.

What’s wild is how clean this makes your architecture. You stop thinking in terms of "big context blob" and start building modular, reactive slices of state. It feels closer to how modern state libraries like Zustand work.

Bonus: use-context-selector is small (\~1KB), tree-shakeable, and plays well with TypeScript. I’ve used it in both greenfield projects and gnarly legacy refactors. Never once regretted it.

Alternatives and DIY Workarounds (If You Hate Dependencies)

Now, let’s say you're allergic to extra libraries. Fair. You can fake it using memoized values and split providers.

javascript
1
2
3
4
5
6
7
8
9
10
11
12
          const ThemeContext = createContext();

function ThemeProvider({ children }) {
  const [theme, setTheme] = useState("light");
  const value = useMemo(() => ({ theme, setTheme }), [theme]);

  return (
    <ThemeContext.Provider value={value}>
      {children}
    </ThemeContext.Provider>
  );
}
        

Then just consume theme in isolation:

javascript
1
          const theme = useContext(ThemeContext).theme;
        

It’s not as elegant or surgical as useContextSelector, but it gets the job done for simpler apps. Just don’t try this with dynamic dashboards or real-time updates. You’ll drown in unnecessary renders.

Also worth noting: React’s team is actively exploring selector-style context natively. Until then, this library is your best option.

Real-Life Example: Redux-Like Context (Without the Trauma)

Here’s where useContextSelector shines—complex global state without the Redux boilerplate.

javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
          const AppContext = createContext();

function AppProvider({ children }) {
  const [state, setState] = useState({
    theme: "dark",
    user: { id: 1, name: "Mo" },
    notifications: []
  });

  const value = useMemo(() => ({ state, setState }), [state]);

  return (
    <AppContext.Provider value={value}>
      {children}
    </AppContext.Provider>
  );
}
        

Then in your components:

javascript
1
          const notifications = useContextSelector(AppContext, v => v.state.notifications);
        

No re-render if the user logs in. No re-render if the theme toggles. Just nice, scoped updates.

I used this approach in a fintech dashboard with heavy UI interactions. It replaced Redux + selectors + reselect + thunk in under 100 lines. The result? 40% fewer re-renders, zero boilerplate, and happier devs.

When It’s Overkill (Yes, That’s a Thing)

If your context only holds one stable value—like app language or a static config—don’t bother. Stick to vanilla useContext. No need to introduce selectors just to feel fancy.

But if you're managing multi-key state that changes independently (like auth, UI prefs, form drafts), then yes—go with useContextSelector. It pays for itself on day one.

I’m convinced this should be in React core. Until it is, it’s my go-to pattern for building scalable, high-performance context.

Next up: want to know why React re-renders when it does, and how to stop the chaos? You’ll want to read **When React Re-renders (and Why You Should Care)**. Trust me—it’ll rewire how you think about performance.