Ререндер при зміні контексту
Чому всі споживачі контексту ререндеряться
Коли ти створюєш контекст:
const ThemeContext = createContext();і використовуєш його так:
<ThemeContext.Provider value={theme}>
<Header />
<Footer />
</ThemeContext.Provider>всі компоненти всередині Provider, які роблять:
const theme = useContext(ThemeContext);підписуються на це значення (value).
Як працює механізм
Context.Providerзберігає одне значення -value.- Кожен компонент, що викликає
useContext(ThemeContext), підписується на це значення. - Коли
valueзмінюється (за посиланням), React вважає, що контекст оновився. - У результаті всі підписані компоненти отримують нове значення -> React перерендерює їх з новим контекстом.
Приклад
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 порівнює не вміст контексту, а посилання на об'єкт:
value={{ theme, setTheme }}Кожен рендер створює новий об'єкт -> отже, контекст "змінився".
Як зменшити кількість ререндерів
Якщо твій контекст зберігає кілька значень, але змінюється тільки одне - можна оптимізувати.
1. Розділи контексти
Замість одного великого контексту:
<AppContext.Provider value={{ user, theme, language }}>розділи їх:
<UserContext.Provider value={user}>
<ThemeContext.Provider value={theme}>
<LangContext.Provider value={language}>
<App />
</LangContext.Provider>
</ThemeContext.Provider>
</UserContext.Provider>Тепер зміна теми не викличе ререндер споживачів UserContext.
2. Мемоізуй value за допомогою useMemo
Щоб Provider не створював новий об'єкт при кожному рендері:
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 або розділити контексти |
| Коли це не проблема | Якщо контекст маленький і рідко оновлюється |
| Коли потрібно оптимізувати | Якщо одна зміна тригерить рендер десятків компонентів |
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.