Join the discussion
Comments
Loading comments...
Cookie preferences
We use essential cookies for sign-in and preferences. With your permission, we also use basic analytics to improve lessons.
Join the discussion
Loading comments...
We use essential cookies for sign-in and preferences. With your permission, we also use basic analytics to improve lessons.
Reactがprops drillingなしにコンポーネントツリーに値を伝播させる方法。
Context APIを使うと、propsをすべての階層で受け渡すこと(props drilling)なしに、コンポーネントツリー全体でデータを共有できます。Providerコンポーネントがサブツリーをラップし、値を提供します。子孫コンポーネントはuseContextでその値にアクセスでき、値が変わると自動的に再レンダリングされます。
React.createContext(defaultValue)はProviderとConsumerを持つContextオブジェクトを作成します。defaultValueは、コンポーネントがツリー内の一致するProviderなしにContextを消費するときに使用されます。
Providerコンポーネントはvalue propを受け取ります。サブツリー内のすべてのコンポーネントがこの値にアクセスできます。同じContextの複数のProviderをネストでき、最も近い祖先のProviderが優先されます。
useContext(MyContext)は、呼び出しコンポーネントの上にある最も近いProviderから現在のContext値を返します。コンポーネントをContextの変更に購読します。
Providerのvalue propが(参照として)変更されると、ReactはサブツリーでuseContext(MyContext)を呼び出すすべてのコンポーネントを再レンダリングします — 消費された値が実際に変わったかどうかに関係なく。
これが重要な効率性です:useContextを呼び出さない中間コンポーネントは再レンダリングされません。Contextはそれらをスキップします。実際のConsumerのみが再レンダリングされます。
Contextオブジェクトを作成します。ツリー内のConsumerの上にProviderがない場合に使用されるデフォルト値を受け取ります。
すべての子孫ConsumerにValueをブロードキャストします。ネストされたProviderは外側のものをオーバーライドします。不要な再レンダリングを防ぐためにValueをメモ化すべきです。
コンポーネントをContextに購読するHook。Context値が変わるたびに再レンダリングします。
ContextはConsumerに直接スキップします — useContextを呼び出さない限り、中間コンポーネントは再レンダリングされません。
ReactはObject.is()でContext値を比較します。レンダリングのたびに新しいオブジェクトリテラル'{''}'をProviderに渡すと、内容が同じでもすべてのConsumerが再レンダリングされます。
1const ThemeContext = React.createContext('light'); // default23// Provider — wrap at the top level4function App() {5 const [theme, setTheme] = useState('dark');67 // ⚠️ Memoize the value to avoid re-rendering all consumers8 // on every App render, even when theme hasn't changed9 const value = useMemo(() => ({ theme, setTheme }), [theme]);1011 return (12 <ThemeContext.Provider value={value}>13 <Layout />14 </ThemeContext.Provider>15 );16}1718// Consumer — anywhere in the subtree19function Button() {20 const { theme } = useContext(ThemeContext);21 return <button className={theme}>Click</button>;22}
Contextはprops drilling — データを深くネストされた子に届けるためだけに多くのコンポーネント層を通じてpropsを渡すこと — を解決します。ただしContextは真にグローバルまたは広く共有されるデータ(認証、テーマ、ロケール)に最適です。頻繁に変わる値にはZustandやReduxを使い、変更のたびにすべてのConsumerが再レンダリングされるのを避けましょう。
Your app has a global theme context providing colors, fonts, and dark/light mode. After adding it, every keystroke in a form input re-renders the entire app because the ThemeProvider is at the root.
The context value is `{ theme, toggleTheme }`. If the ThemeProvider's parent re-renders for ANY reason (new route, state change), a new object is created → all consumers re-render. With 50+ themed components, every parent re-render triggers 50+ unnecessary re-renders.
Split into two contexts: `ThemeValueContext` (read-only, changes only on toggle) and `ThemeDispatchContext` (stable setter function). Components that only READ the theme subscribe to ThemeValueContext. Components that only TOGGLE subscribe to ThemeDispatchContext. Memoize both values with useMemo.
Takeaway: Split contexts by update frequency: separate rarely-changing data (theme colors) from frequently-changing data (user input). This prevents the 'context-triggers-everything' problem and is the pattern used by Redux, Zustand, and Jotai internally.
A settings page passes `locale`, `currency`, and `dateFormat` through 6 levels of nesting: Settings → Layout → Panel → Section → Field → Label. Adding a new preference requires editing 6 components.
Prop drilling creates tight coupling — every intermediate component must know about and forward props it doesn't use. Adding, renaming, or removing a preference requires changing every component in the chain. The intermediate components also re-render when props change even though they don't USE the data.
Create a `PreferencesContext` and consume it directly in Label with `useContext(PreferencesContext)`. Intermediate components (Layout, Panel, Section, Field) don't receive or forward preference props. The preferences data 'teleports' from Provider to consumer, skipping all intermediate components.
Takeaway: Context eliminates prop drilling for cross-cutting concerns (theme, auth, locale, feature flags). Use it when data needs to be available deep in the tree and passing it through 3+ levels of components that don't use it. For frequently-updating state, consider a state manager instead.