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がHooksをリンクリストとして管理し、状態更新を処理して再レンダリングする仕組み。
HooksはFiberノード上の単方向リンクリストとして格納されます。コンポーネントがレンダリングされると、Reactはこのリストを順番に走査し、各Hook呼び出し(useState、useEffect、useRefなど)を格納されたstateにマッチングします。これがHooksのルールが存在する理由です:条件付きでHooksを呼び出すと、リストの順序が変わり、Reactが間違ったHookの間違ったstateを読み取ります。
各Hook呼び出し(useState、useEffect、useRef、useMemo…)は、memoizedStateフィールドとnextポインタを持つ「Hook」オブジェクトを作成します。ReactはそれぞれをCall順にリストに追加します。リストはFiberノードのmemoizedStateフィールドに格納されます。
useStateのHookオブジェクトは次を格納します:memoizedState(現在の値)、queue(保留中のsetStateCallのリンクリスト)、dispatch(コンポーネントで使用するsetState関数)。dispatch関数は安定しています — レンダリング間で変化しません。
setState(newValue)を呼び出しても、Reactはすぐにstateを更新しません。代わりに、UpdateオブジェクトをHookのキューに追加し、Fiberの再レンダリングをスケジュールします。1つのイベントハンドラ内の複数のsetState呼び出しは1回の再レンダリングにバッチされます。
次のレンダリングでは、ReactはHooksリストを再度走査します。各useStateフックに対して、キューに入っているすべての更新を順番に実行(またはuseReducerのreducer関数を呼び出す)して処理します。最終的に計算された値が新しいmemoizedStateになります。
エフェクトHookオブジェクトは次を格納します:create(渡したコールバック)、destroy(前回返したクリーンアップ関数)、deps(依存配列)、それがuseEffect、useLayoutEffect、useInsertionEffectであるかを示すタグ。
HooksはFiberノード上の単方向リンクリストとして格納されます。呼び出し順 = リスト順。条件付きHooksはリストを壊します。
Hookオブジェクトの現在の値を格納するフィールド。useStateの場合:state値。useRefの場合:'{' current: value '}'。useMemoの場合:キャッシュされた結果。
次のレンダリングで処理される保留中のsetStateCallを格納する循環リンクリスト。複数の更新は1回のキューフラッシュにバッチされます。
ReactはレンダーフェーズによってHookの実装を切り替えます。最初のレンダリング時:HooksDispatcherOnMount。再レンダリング時:HooksDispatcherOnUpdate。これがHooksをコンポーネント外で呼び出せない理由です。
Hooksはトップレベルのみで呼び出す(条件・ループ内は不可)、Reactの関数からのみ呼び出す。これらのルールはReactがリンクリスト内の位置でHooksを識別するために存在します。
1// Your component:2function Counter() {3 const [count, setCount] = useState(0); // Hook 14 const [name, setName] = useState('Bob'); // Hook 25 const ref = useRef(null); // Hook 367 useEffect(() => { ... }, [count]); // Hook 48}910// React's internal hooks linked list for this fiber:11Hook1 { memoizedState: 0, next: Hook2 } // count12Hook2 { memoizedState: 'Bob', next: Hook3 } // name13Hook3 { memoizedState: { current: null }, next: Hook4 } // ref14Hook4 { memoizedState: { create, destroy, deps: [0] }, next: null }1516// If you conditionally call hook 2:17// Hook1 → Hook3 → Hook4 (Hook2 slot is now useRef!)18// React reads "Bob" as the ref's current value → BUG
Hooksの内部を理解することで、HooksのルールがなぜRestrictionsではなく技術的必要性であるかが説明できます。また、useStateのsetter関数がなぜ常に安定しているか(値ではなくキューに紐づいているため)、Hooksがレンダリングをまたいでなぜ機能するか(Fiberに永続されているため)、カスタムHooksがなぜ構築できるか — 単に他のHooksを呼び出す関数でリストにさらにノードを追加するだけだからです。
You add an early return in a component for a loading state: `if (loading) return <Spinner />`. After the data loads, React crashes with 'Rendered more hooks than during the previous render.'
The early return is ABOVE some hook calls. On the first render (loading=true), React processes 2 hooks. On the second render (loading=false), it processes 5 hooks. React's linked list has 2 slots but encounters 5 hook calls — the list is corrupted.
Move ALL hook calls above any conditional returns. The early return must come AFTER the last hook call. React hooks are a linked list indexed by call order — the call count must be identical across every render of the same component.
Takeaway: The 'Rules of Hooks' aren't arbitrary restrictions — they're a direct consequence of the linked list data structure. React identifies hooks by position (call order), not by name. Changing the number of hooks between renders corrupts the list.
You build a custom `useToggle` hook used in 3 different components. You notice that toggling in one component doesn't affect the others — but you're confused about WHY, since they share the same hook code.
Developers sometimes assume custom hooks share state like a global singleton. They expect `useToggle()` in Component A and Component B to share the same boolean value, leading to incorrect architecture decisions.
Each component has its own fiber node with its own hooks linked list. When Component A calls `useToggle()`, React creates a hook node in Component A's list. Component B gets a completely separate hook node in its own list. Custom hooks are just a code-sharing mechanism — state is always local to the calling component's fiber.
Takeaway: Custom hooks share LOGIC, not STATE. Each component instance gets its own copy of the hook's state because each fiber maintains its own hooks linked list. To share state between components, you need Context, a state manager, or lifting state up.