Передавай данные без prop drilling с помощью контекста, держа под контролем его перерисовки и ограничения.
Открыть этот урок в KodokonProp drilling - это те пропсы, которые проходят через три-четыре промежуточных компонента, не используясь в них, только чтобы добраться до листа дерева. Каждый слой, который они пересекают, оказывается связан с данными, до которых ему нет дела. Контекст решает эту проблему транспорта: createContext создаёт канал, провайдер публикует значение, а useContext читает его из любого потомка. Держи в голове нюанс: контекст переносит состояние, но не управляет им - значение по-прежнему живёт в компоненте, здесь в провайдере.
import { createContext, useMemo, useState } from "react";
const ThemeContext = createContext(null);
function ThemeProvider({ children }) {
const [theme, setTheme] = useState("dark");
const value = useMemo(
() => ({ theme, setTheme }),
[theme]
);
return (
<ThemeContext.Provider value={value}>
{children}
</ThemeContext.Provider>
);
}Когда значение провайдера меняется, перерисовываются все потребители, даже глубоко вложенные - и React.memo их не защитит, потому что он сравнивает пропсы, а не контекст. Классическая ловушка только усугубляет дело: запись value={{ theme, setTheme }} создаёт новый объект при каждом рендере провайдера, а значит новую ссылку, а значит каскад перерисовок, даже если theme не сдвинулся с места. Именно для этого нужен useMemo выше. Дополни конструкцию хуком доступа, который громко падает за пределами провайдера.
import { useContext } from "react";
function useTheme() {
const context = useContext(ThemeContext);
if (context === null) {
throw new Error(
"useTheme must be inside <ThemeProvider>"
);
}
return context;
}Прежде чем создавать контекст, исчерпай вариант с композицией. Если промежуточный слой только передаёт пропс дальше, переверни структуру: собери элемент наверху дерева, там, где живут данные, и передай его через children или отдельный пропс. Промежуточный слой становится универсальным, а данные больше не проходят ни через один компонент, который их не использует.
function App({ currentUser }) {
return (
<Layout
header={<TopBar user={currentUser} />}
content={<Dashboard />}
/>
);
}