Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Ререндер дочірніх елементів». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)Коли змінюється значення, передане в `<Context.Provider value={...}>`, **React перемальовує всі компоненти**, які використовують цей контекст через `useContext()` або `<Context.Consumer>`. **Ключове:** React порівнює нове значення з попереднім **за посиланням, а не за вмістом**, тому навіть однаковий за змістом об'єкт, створений заново, викликає ререндер усіх підписників.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення## Коротко Коли змінюється значення, передане в `<Context.Provider value={...}>`, React **перемальовує всі компоненти**, які використовують цей контекст через `useContext()` або `<Context.Consumer>`. --- ## Чому це відбувається Тому що **Context - це глобальний механізм підписки**. React робить приблизно наступне: 1. У кожного контексту є *список споживачів* (components subscribed). 2. Коли Provider отримує нове значення `value`, React **порівнює нове значення з попереднім (за посиланням)**. 3. Якщо посилання змінилося (`!==`), він **сигналізує всім підписникам**, що "значення змінилося". 4. Кожен компонент, що використовує `useContext()`, **виконує повторний рендер**, щоб отримати нове значення. --- ## Головне: порівняння йде **за посиланням, не за вмістом** ```javascript <ThemeContext.Provider value={{ mode: "dark" }}> <App /> </ThemeContext.Provider> ``` Навіть якщо `mode` не змінився, при кожному рендері `App` створюється **новий об'єкт** (`{ mode: "dark" } !== { mode: "dark" }`), і React думає, що значення контексту змінилося -> всі підписники перемальовуються. --- ## Як цього уникнути - мемоізація `value` Потрібно передавати **стабільне посилання** за допомогою `useMemo`: ```javascript function ThemeProvider({ children }) { const [mode, setMode] = useState("light"); const value = useMemo(() => ({ mode, setMode }), [mode]); return <ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>; } ``` Тепер об'єкт `value` створюється **тільки при зміні** `mode`, а не при кожному рендері - і контекст не викликає каскадний ререндер даремно. --- ## Аналогія Можна уявити, що `Provider` - це "маяк", який каже всім підписникам: > "Гей, значення змінилося, оновіться!" React не перевіряє, *що саме* змінилося всередині об'єкта - він просто бачить, що "маяк подав новий сигнал" (нове посилання), і оновлює всіх підписників. --- ## До чого призводить "необережне" використання контексту | Проблема | Приклад | |---|---| | Масові ререндери | Один Provider оновлює 100+ компонентів, і всі ререндеряться | | Зайві обчислення | Компоненти отримують те саме значення, але все одно перераховуються | | Втрата продуктивності | Особливо при передачі "товстих" об'єктів у `value` | --- ## Способи оптимізації 1. **Розділяй контексти** Не зберігай усе в одному великому контексті - створюй незалежні: `AuthContext`, `ThemeContext`, `UIContext` тощо. 2. **Використовуй** `useMemo` **для value** ```javascript const value = useMemo(() => ({ user, setUser }), [user]); ``` 3. **Ізолюй "важких" споживачів** Якщо контекст часто оновлюється - обгортай частину UI в `memo` або виноси окремі Providers. 4. **Використовуй селектори** (наприклад, з бібліотекою [use-context-selector](https://github.com/dai-shi/use-context-selector)) Вона дозволяє компоненту підписуватися **тільки на конкретне поле** контексту, а не на все `value`. --- ## Підсумок Оновлення контексту викликає ререндер усіх споживачів, тому що React **підписує їх на одне спільне значення** і відстежує його **за посиланням**. Це поведінка за дизайном - Context не призначений для "дрібночастотних" даних. Щоб оптимізувати: - мемоізуй `value`, - ділі контексти, - використовуй спеціалізовані рішення (`use-context-selector`, Zustand, Jotai тощо)Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.