Пошук уроків, статей та іншого контенту
Як уникнути передачі props через десяток проміжних компонентів, яким вони не потрібні.
У глибоко вкладеному дереві компонентів дані іноді потрібні лише в найглибшому компоненті, але щоб туди дістатись, їх доводиться передавати як props через кожен проміжний компонент — навіть ті, яким ці дані самі по собі не потрібні, вони лише «пропускають» їх далі. Це і називають props drilling: чим глибше дерево, тим більше проміжних компонентів захаращені props, які вони самі не використовують.
function App() {
const [theme, setTheme] = useState("dark");
return <Page theme={theme} />;
}
function Page({ theme }) {
return <Sidebar theme={theme} />; // Page сам не використовує theme
}
function Sidebar({ theme }) {
return <ThemeToggle theme={theme} />; // Sidebar теж лише передає далі
}
function ThemeToggle({ theme }) {
return <button>Поточна тема: {theme}</button>; // тут theme нарешті потрібенContext дозволяє «прокинути» значення напряму від компонента-провайдера до будь-якого нащадка на будь-якій глибині, без явної передачі через кожен проміжний рівень. Створення й використання складається з трьох кроків:
// 1. Створити контекст (зазвичай в окремому файлі)
const ThemeContext = createContext("light");
// 2. Обгорнути піддерево провайдером зі значенням
function App() {
const [theme, setTheme] = useState("dark");
return (
<ThemeContext.Provider value={theme}>
<Page />
</ThemeContext.Provider>
);
}
// 3. Прочитати значення в будь-якому нащадку через useContext
function ThemeToggle() {
const theme = useContext(ThemeContext); // без жодних проміжних props
return <button>Поточна тема: {theme}</button>;
}Компоненти Page і Sidebar між App і ThemeToggle більше не потребують знати про theme узагалі — Context «пробиває» їх наскрізь.
Context добре підходить для даних, що дійсно потрібні широкому й непередбачуваному піддереву компонентів: поточна тема, залогований користувач, мова інтерфейсу. Він не призначений замінити всю передачу props узагалі — для локальних, сусідніх компонентів звичайні props і далі найпростіший і найявніший спосіб передати дані.
Продуктивність: коли значення в Provider змінюється, усі компоненти, що читають цей контекст через useContext, перерендеряться — незалежно від того, яку саме частину значення вони реально використовують. Класта пастка — класти в один контекст і рідкозмінне значення (тема), і часто змінюване (наприклад, позиція курсора): кожна зміна курсора змусить перерендеритись усі споживачі теми теж.
Використання Context як загальної заміни всього керування станом застосунку — для складної, часто змінюваної бізнес-логіки зазвичай краще підходять спеціалізовані інструменти керування станом, а Context лишити для порівняно стабільних, «широких» значень.
Один величезний контекст із десятками непов'язаних значень замість кількох невеликих, тематичних контекстів — будь-яка зміна в такому контексті перерендерює всіх споживачів усіх його полів одразу.
Забути обгорнути частину дерева в Provider — useContext поверне значення за замовчуванням, вказане при createContext(defaultValue), що часто непомітно веде до неочевидних багів замість явної помилки.
Context вирішує props drilling — необхідність передавати дані через проміжні компоненти, яким вони самі не потрібні, — дозволяючи будь-якому нащадку читати значення провайдера напряму через useContext. Він підходить для порівняно стабільних, широко потрібних даних (тема, автентифікація, локалізація); зміна значення контексту перерендерює всіх його споживачів, тому часто змінювані дані краще тримати поза Context або в окремому, вужчому контексті.