Skip to main content

Ререндер при зміні контексту

Чому всі споживачі контексту ререндеряться

Коли ти створюєш контекст:

javascript
const ThemeContext = createContext();

і використовуєш його так:

javascript
<ThemeContext.Provider value={theme}> <Header /> <Footer /> </ThemeContext.Provider>

всі компоненти всередині Provider, які роблять:

javascript
const theme = useContext(ThemeContext);

підписуються на це значення (value).


Як працює механізм

  1. Context.Provider зберігає одне значення - value.
  2. Кожен компонент, що викликає useContext(ThemeContext), підписується на це значення.
  3. Коли value змінюється (за посиланням), React вважає, що контекст оновився.
  4. У результаті всі підписані компоненти отримують нове значення -> React перерендерює їх з новим контекстом.

Приклад

javascript
const ThemeContext = createContext(); function App() { const [theme, setTheme] = useState("light"); return ( <ThemeContext.Provider value={{ theme, setTheme }}> <Header /> <Footer /> </ThemeContext.Provider> ); }

Коли викликається setTheme("dark"), value={{ theme, setTheme }} стає новим об'єктом, і React бачить, що посилання на value змінилося, -> всі компоненти з useContext(ThemeContext) ререндеряться.


Чому це важливо

React порівнює не вміст контексту, а посилання на об'єкт:

javascript
value={{ theme, setTheme }}

Кожен рендер створює новий об'єкт -> отже, контекст "змінився".


Як зменшити кількість ререндерів

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


1. Розділи контексти

Замість одного великого контексту:

javascript
<AppContext.Provider value={{ user, theme, language }}>

розділи їх:

javascript
<UserContext.Provider value={user}> <ThemeContext.Provider value={theme}> <LangContext.Provider value={language}> <App /> </LangContext.Provider> </ThemeContext.Provider> </UserContext.Provider>

Тепер зміна теми не викличе ререндер споживачів UserContext.


2. Мемоізуй value за допомогою useMemo

Щоб Provider не створював новий об'єкт при кожному рендері:

javascript
const value = useMemo(() => ({ theme, setTheme }), [theme]); return <ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>;

Тепер посилання на value стабільне - контекст оновиться тільки при зміні theme, а не при кожному рендері батька.


3. Розмісти споживачів глибше

Якщо контекст використовується тільки в частині UI, розмісти Provider ближче до місця використання, щоб менше компонентів залежало від нього.


Як думає React

React діє за принципом:

"Якщо значення контексту змінилося - я маю оновити всі компоненти, які його використовують, щоб вони відображали актуальні дані."

Це гарантує узгодженість UI, але може створювати зайві ререндери, якщо не оптимізувати.


Підсумок

ПитанняВідповідь
Чому всі споживачі ререндерятьсяТому що у Provider.value змінилося посилання (React вважає, що значення нове)
Що саме React перевіряєНе вміст, а посилання (Object.is(valueOld, valueNew))
Як уникнутиМемоізувати value через useMemo або розділити контексти
Коли це не проблемаЯкщо контекст маленький і рідко оновлюється
Коли потрібно оптимізуватиЯкщо одна зміна тригерить рендер десятків компонентів

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.