Upgrade to Pro — share decks privately, control downloads, hide ads and more …

React Hooksの実装を探検してみる / Explore the React Hooks implementation

React Hooksの実装を探検してみる / Explore the React Hooks implementation

193Otani

July 28, 2022
Tweet

Other Decks in Programming

Transcript

  1. Reactとは? • .FUB چ'BDFCPPL ͱίϛϡχςΟʹΑͬͯ։ൃ͞Ε͍ͯΔɺ6*ߏஙͷͨΊ ͷKBWBTDSJQUϥΠϒϥϦ  (JUIVC GBDFCPPLSFBDU •

    ҎԼͷͭΛίϯηϓτͱͯ͠։ൃ͞Ε͍ͯΔ ◦ એݴతͳ7JFX ◦ ίϯϙʔωϯτϕʔε ◦ Ұ౓ֶश͢Ε͹ɺͲ͜Ͱ΋࢖͑Δ • 8FCϒϥ΢β޲͚ͷ6*։ൃʹՃ͑ɺ3FBDU/BUJWFΛ༻͍ͨϞόΠϧΞϓϦ ͷ։ൃʹ΋ར༻Ͱ͖Δ
  2. useEffectとは? • 関数コンポーネント内で副作用を実行することができる ◦ 副作用とは? - ある処理を実行した結果他の処理に対して影響を伴うようなもの ◦ stateの値を更新する、APIの値を取得するなど •

    useEffectに渡された副作用関数は画面のレンダリング後に発火される • クラスコンポーネントにおける以下のライフサイクルメソッドの代替機能 ◦ componentDidMount, componentDidUpdate, componentWillUnmount
  3. useEffectの本体 • react/src/ReactHooks.js • ReactCurrentDispatcher.current.useEffect(create, deps)を返している ◦ create -> useEffectで実行させたい副作用関数

    ◦ deps -> 副作用関数が依存している変数の配列 // @flow export function useEffect( create: () => (() => void) | void, deps: Array<mixed> | void | null, ): void { const dispatcher = resolveDispatcher(); return dispatcher.useEffect(create, deps); } // @flow import ReactCurrentDispatcher from './ReactCurrentDispatcher'; function resolveDispatcher() { const dispatcher = ReactCurrentDispatcher.current; … // コメント省略 return ((dispatcher: any): Dispatcher); }
  4. ReactCurrentDispatcherを見る • ReactCurrentDispatcher.js • currentの中身空っぽいんだけど? ◦ Dispatcher型がセットされるようだが...? ◦ react-reconcilerってなんだ? //

    @flow import type {Dispatcher} from 'react- reconciler/src/ReactInternalTypes'; const ReactCurrentDispatcher = { /** * @internal * @type {ReactComponent} */ current: (null: null | Dispatcher), }; export default ReactCurrentDispatcher;
  5. 結局、ReactCurrentDispatcher.currentの中身は? • renderWithHooks - ReactFiberHooks.new.js: L.373 • currentの中身はHooksDispatcherOnMount or HooksDispatcherOnUpdate

    ◦ 各Hookに対応した関数を持つオブジェクト ◦ OnMount: L.2420 ◦ OnUpdate: L. 2427 // ReactFiberHooks.new.js - L.373 … if (__DEV__) { ... } else { ReactCurrentDispatcher.current = current === null || current.memoizedState === null ? HooksDispatcherOnMount : HooksDispatcherOnUpdate; } …
  6. HooksDispatcherOnMount • 各Hookに対応する初期化関数をもっている • OnUpdateの場合は更新関数が割り当てら れている ◦ useEffect: updateEffect const

    HooksDispatcherOnMount: Dispatcher = { readContext, useCallback: mountCallback, useContext: readContext, useEffect: mountEffect, useImperativeHandle: mountImperativeHandle, …, useId: mountId, unstable_isNewReconciler: enableNewReconciler, };
  7. mountEffect(...) • 特別な処理はなさそう • mounEffectImplに渡しているフラグが気になる // ReactFiberHooks.new.jp - L.1701 function

    mountEffect( create: () => (() => void) | void, deps: Array<mixed> | void | null, ): void { if ( __DEV__ && … ) { return mountEffectImpl(...); } else { return mountEffectImpl( PassiveEffect | PassiveStaticEffect, HookPassive, create, deps, ); } }
  8. mountEffectImpl(...) • mountWorkInProgressHook(...) - L.635 ◦ Effect Hookの状態管理ツリーに現在の状態が保存されたノ ードを追加 ◦

    上記ノードが持っている情報ごと • pushEffect(...) - L. 1543 ◦ 現在のeffectの状態を保存したEffectノードを作成 し、Fiberが持っている更新キューに追加 ◦ 保存される情報には依存配列の状態を含む // ReactFiberHooks.new.js - L. 1662 function mountEffectImpl(fiberFlags, hookFlags, create, deps): void { const hook = mountWorkInProgressHook(); const nextDeps = deps === undefined ? null : deps; currentlyRenderingFiber.flags |= fiberFlags; hook.memoizedState = pushEffect( HookHasEffect | hookFlags, create, undefined, nextDeps, ); }
  9. updateEffectImplに秘密が... • 入力される依存配列が過去に使用したものと一致するかチェックしている • 一致した場合、flagsの更新をしていない = useEffectの動作と一致している // L. 1674

    const prevDeps = prevEffect.deps; if (areHookInputsEqual(nextDeps, prevDeps)) { hook.memoizedState = pushEffect(hookFlags, create, destroy, nextDeps); return; } } } currentlyRenderingFiber.flags |= fiberFlags; // 副作用を適用するフラグを立てる
  10. 参考 • React-Fiber-Architecture • Reconciliation - React • An Introduction

    to React Fiber - The Algorithm Behind React • React Fiberとその周りについて調べた