Avoid re-renders

2 snippets in React

REReact

Avoiding Unnecessary Re-renders

RE · useContext
Syntax
// Memoize the value object
const value = useMemo(() => ({ data, handler }), [data]);

// Split read-only data from dispatch
<DataContext.Provider value={data}>
  <DispatchContext.Provider value={dispatch}>
Example
import { createContext, useContext, useMemo, useState } from 'react';

const CountContext = createContext(null);
const CountDispatchContext = createContext(null);

function CountProvider({ children }) {
  const [count, setCount] = useState(0);

  // dispatch never changes identity
  const increment = useMemo(
    () => () => setCount(c => c + 1),
    []
  );

  return (
    <CountContext.Provider value={count}>
      <CountDispatchContext.Provider value={increment}>
        {children}
      </CountDispatchContext.Provider>
    </CountContext.Provider>
  );
}

// Components reading only dispatch won't re-render when count changes
function IncrementButton() {
  const increment = useContext(CountDispatchContext);
  return <button onClick={increment}>+1</button>;
}

Note Splitting context into a data context and a dispatch context is a powerful pattern. Components that only call actions (dispatch) will not re-render when the data changes, because the dispatch reference stays stable.

Avoiding Unnecessary Renders

RE · Performance
Syntax
// 1. Move state down (closer to where it is used)
// 2. Lift content up (pass components as children)
// 3. Memoize with React.memo + useCallback/useMemo
Example
// PROBLEM: typing in input re-renders the expensive list
function BadPage() {
  const [query, setQuery] = useState('');
  return (
    <div>
      <input value={query} onChange={e => setQuery(e.target.value)} />
      <ExpensiveList /> {/* re-renders on every keystroke */}
    </div>
  );
}

// FIX 1: Move state down into its own component
function GoodPage() {
  return (
    <div>
      <SearchInput /> {/* state lives here now */}
      <ExpensiveList /> {/* no longer re-renders on keystrokes */}
    </div>
  );
}

function SearchInput() {
  const [query, setQuery] = useState('');
  return <input value={query} onChange={e => setQuery(e.target.value)} />;
}

// FIX 2: Lift content up via children
function InputWrapper({ children }) {
  const [query, setQuery] = useState('');
  return (
    <div>
      <input value={query} onChange={e => setQuery(e.target.value)} />
      {children} {/* children don't re-render */}
    </div>
  );
}

function Page() {
  return (
    <InputWrapper>
      <ExpensiveList />
    </InputWrapper>
  );
}

Note Before reaching for React.memo, try restructuring your component tree. Moving state down and lifting content up are free optimizations that often eliminate the problem entirely.

Frequently asked questions

How do you avoid re-renders?
React covers this with 2 copy-ready snippets on this page. The "Avoiding Unnecessary Re-renders" snippet in React uses `// Memoize the value object`.
Which code does the React example use?
The "Avoiding Unnecessary Re-renders" snippet uses `// Memoize the value object`, from the useContext section of the React cheat sheet.
What other React snippets are shown for "avoid re-renders"?
Besides "Avoiding Unnecessary Re-renders", this page also shows "Avoiding Unnecessary Renders".
Is there anything to watch out for?
Yes. For "Avoiding Unnecessary Re-renders": Splitting context into a data context and a dispatch context is a powerful pattern. Components that only call actions (dispatch) will not re-render when the data changes, because the dispatch reference stays stable.