context を使って prop drilling なしでデータを行き渡らせ、その再レンダーと制約を管理下に置きます。
このレッスンを Kodokon で開くProp drilling とは、そこでは使われないのに 3 つ 4 つの中間コンポーネントを通り抜け、ツリーの葉に届くためだけに存在する props のことです。通過する層はどれも、興味のないデータと結合してしまいます。context はこの輸送の問題を解決します。createContext がチャネルを作り、プロバイダーが値を公開し、useContext が任意の子孫からそれを読み取ります。ニュアンスを忘れないでください。context は state を 輸送 するのであって、管理はしません。値は依然としてコンポーネント、ここではプロバイダーの中に存在します。
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 は守ってくれません。props は比較しても context は比較しないからです。典型的な罠がこれを悪化させます。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;
}context を作る前に、まず コンポジション の選択肢を尽くしましょう。中間層が prop を中継するだけなら、構造を逆転させます。データが存在するツリーの上部で要素を組み立て、children や専用の prop を通じて下へ渡すのです。中間層は汎用になり、データはそれを使わないコンポーネントを一切通らなくなります。
function App({ currentUser }) {
return (
<Layout
header={<TopBar user={currentUser} />}
content={<Dashboard />}
/>
);
}