Увійти Реєстрація
Блог Серії
Кар'єра
Вакансії Компанії
Навчання
Документація Співбесіди Тестування Відео
Екосистема
Пакети Ресурси Проєкти Інструменти Події
Інше
Про нас Реклама

Як уникнути перерендеру всіх споживачів контексту при кожній зміні значення?

Коли змінюється значення провайдера контексту, React перерендерює всі компоненти, що читають цей контекст, - навіть якщо вони використовують поле, яке не змінилося. memo на споживачі від цього не захищає.

Пастка 1 - новий об'єкт на кожен рендер провайдера:

function AuthProvider({ children }) {
  const [user, setUser] = useState(null);
  const login = async (data) => { /* ... */ };

  // новий об'єкт і нова функція при КОЖНОМУ рендері AuthProvider
  return <AuthContext value={{ user, login }}>{children}</AuthContext>;
}

Будь-який рендер провайдера (наприклад, через стан вище в дереві) змушує всіх споживачів рендеритися. Рішення - стабільне значення:

const login = useCallback(async (data) => { /* ... */ }, []);
const value = useMemo(() => ({ user, login }), [user, login]);
return <AuthContext value={value}>{children}</AuthContext>;

React Compiler робить цю мемоізацію автоматично.

Пастка 2 - один контекст для даних, що змінюються з різною частотою. Тема, користувач і стан кошика в одному контексті: додавання товару перерендерює все, що читає тему.

Рішення - розділити контексти:

<ThemeContext value={theme}>
  <UserContext value={user}>
    <CartContext value={cart}>{children}</CartContext>
  </UserContext>
</ThemeContext>

Розділити стан і дії: компоненти, що лише змінюють стан (кнопка «Додати в кошик»), не мають рендеритися, коли стан змінюється:

<CartStateContext value={items}>
  <CartActionsContext value={actions}>   {/* стабільний об'єкт, не змінюється */}
    {children}
  </CartActionsContext>
</CartStateContext>

Коли контексту замало. Контекст - механізм передачі значення, а не сховище з підпискою на частину стану. Для великого стану, що часто змінюється (редактор, складна форма, дані в реальному часі), краще бібліотеки з селекторами - Zustand, Jotai, Redux Toolkit, useSyncExternalStore: компонент підписується лише на потрібне поле й рендериться лише при його зміні.

Що варто пам'ятати: контекст добре підходить для рідко змінюваних даних - тема, мова, поточний користувач, налаштування. Проблеми з продуктивністю зазвичай починаються, коли в контекст кладуть швидко змінюваний стан.

Докладніше в документації: useContext: оптимізація рендерів

Перевір себе

20 випадкових питань за спробу, після завершення - розбір кожної помилки

Схожі питання