Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
React Hooksの実装を探検してみる / Explore the React Hooks...
Search
193Otani
July 28, 2022
Programming
130
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
React Hooksの実装を探検してみる / Explore the React Hooks implementation
193Otani
July 28, 2022
Other Decks in Programming
See All in Programming
Intent as Code
shoppingjaws
0
130
Family mrubyの進捗
kishima
1
100
デプロイ直後のレイテンシスパイクを調べたら、 Railsの仕様にたどり着いた
nhsykym
0
110
AI時代に学ぶ 好きなルール 嫌いなルール Linter編
shorty5121
0
880
go-spidermonkeyでAIエージェントのCode Modeを実装する
syumai
3
1.5k
AI駆動開発にグラフDBを重ねてみた
satoshi256kbyte
2
760
初心者DevRelとして参加者だった私が、DevRel Talks!#2に登壇するまでにしてきたこと
sokohirai
0
340
レビュー履歴をAIに食わせて、 Compose移行を加速するs
shihochan
0
270
変化を抱擁するドキュメントの作り方 - ビジネスルール駆動開発がもたらす、コードとの新しい関係
ioki
2
120
AIの中の人になってみる
htkym
0
170
GKE で Pod の見方を変えたら、スケールアウト時の挙動を真に捉えられた話
stkk
0
100
FastAPI の並行処理モデルを完全に理解する
hoto17296
9
3.8k
Featured
See All Featured
Agile Leadership in an Agile Organization
kimpetersen
PRO
0
230
What the history of the web can teach us about the future of AI
inesmontani
PRO
1
680
Templates, Plugins, & Blocks: Oh My! Creating the theme that thinks of everything
marktimemedia
31
2.9k
技術選定の審美眼(2025年版) / Understanding the Spiral of Technologies 2025 edition
twada
PRO
120
120k
Designing for humans not robots
tammielis
254
26k
The Organizational Zoo: Understanding Human Behavior Agility Through Metaphoric Constructive Conversations (based on the works of Arthur Shelley, Ph.D)
kimpetersen
PRO
0
440
The Illustrated Children's Guide to Kubernetes
chrisshort
51
53k
Refactoring Trust on Your Teams (GOTO; Chicago 2020)
rmw
35
3.8k
Why Our Code Smells
bkeepers
PRO
340
58k
Statistics for Hackers
jakevdp
799
230k
エンジニアに許された特別な時間の終わり
watany
108
250k
Designing Dashboards & Data Visualisations in Web Apps
destraynor
232
55k
Transcript
React Hooksの実装を探検してみる 株式会社カオナビ プロダクト本部 サービス開発部 Data Frontier グループ 大谷 育未
自己紹介 # 経歴 • 2020年4月にカオナビに新卒入社 • 2年目からフロントエンドエンジニアとして本格的にスタート • 現在は機能拡充を中心としたチームにて活動中 •
趣味はダーツ、ゲーム、モータースポーツ ◦ 今年はF1がアツいです
目指すこと/目指さないこと # 目指すこと • Hooksが呼び出されたときReactがなにをしているのかをざっくり理解する • 調査の過程で出てきたReact内部の構造、キーワードを知ってもらう # 目指さないこと •
React / React Hooksの動作原理を完璧に理解する • 登場するキーワードについて完全に理解する
Reactとは? • .FUB چ'BDFCPPL ͱίϛϡχςΟʹΑͬͯ։ൃ͞Ε͍ͯΔɺ6*ߏஙͷͨΊ ͷKBWBTDSJQUϥΠϒϥϦ (JUIVC GBDFCPPLSFBDU •
ҎԼͷͭΛίϯηϓτͱͯ͠։ൃ͞Ε͍ͯΔ ◦ એݴతͳ7JFX ◦ ίϯϙʔωϯτϕʔε ◦ Ұֶश͢ΕɺͲ͜Ͱ͑Δ • 8FCϒϥβ͚ͷ6*։ൃʹՃ͑ɺ3FBDU/BUJWFΛ༻͍ͨϞόΠϧΞϓϦ ͷ։ൃʹར༻Ͱ͖Δ
React Hooksとは? • React 16.8から追加されたReactの新機能群 • 関数コンポーネント内でReactのstateやライフサイクルメソッドを使用する ことができる(クラスコンポーネント内では機能しない) • ビルトインですぐに利用できるHooksが用意されている
◦ useState, useEffect, useMemo etc…
今回のターゲットはuseEffect
useEffectとは? • 関数コンポーネント内で副作用を実行することができる ◦ 副作用とは? - ある処理を実行した結果他の処理に対して影響を伴うようなもの ◦ stateの値を更新する、APIの値を取得するなど •
useEffectに渡された副作用関数は画面のレンダリング後に発火される • クラスコンポーネントにおける以下のライフサイクルメソッドの代替機能 ◦ componentDidMount, componentDidUpdate, componentWillUnmount
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); }
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;
React-Reconcilerってなに? • Fiberという単位でコンポーネントの状態や更新情報を管理している ◦ Fiberは一つのコンポーネントに対して基本的に1:1で存在する ◦ FiberのTreeを構成し、さらに変更前と変更後の二つのTreeを比較することで、更 新が必要な差分の検出を行っている(Reconciliation) • Reconcilerはこの差分検出処理群
• コアロジックを共有することで、Webアプリやネイティブアプリにおける動 作の一貫性を保っている ◦ ブラウザ開発開発: React + React DOM ◦ モバイル向け開発: React + React Native
ReactにおけるFiberとは? • コンポーネントの状態や仮想DOM情報をもつオブジェクト • コンポーネントに対してFiberは必ず1つは存在している • ReactはFiberを連結リストとして持つことで、コンポーネントごとに状態や 更新管理を行うことができる
Reconcilerが行う差分検出のイメージ Reconciliation Current WorkInProgress Time 1 Time 2
結局、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; } …
HooksDispatcherOnMount • 各Hookに対応する初期化関数をもっている • OnUpdateの場合は更新関数が割り当てら れている ◦ useEffect: updateEffect const
HooksDispatcherOnMount: Dispatcher = { readContext, useCallback: mountCallback, useContext: readContext, useEffect: mountEffect, useImperativeHandle: mountImperativeHandle, …, useId: mountId, unstable_isNewReconciler: enableNewReconciler, };
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, ); } }
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, ); }
useEffectの初回呼び出し 1. useEffect(...)が呼び出されると最終的にmountEffectImpl(...)が呼ばれる 2. 新しいHookが実行されること、そのHookがEffect Hookによる更新であるこ とがFiberに伝えられる
2回目以降の動きはどうなるのか? • useEffectは基本的に毎レンダリング呼び出される • useEffectは引数として依存する変数の配列を渡すことができる • 依存配列の中身が変わらない場合はuseEffectの処理は実行されない useEffect(() => {
console.log(effectVal); },[effectVal]);
updateEffectImplに秘密が... • 入力される依存配列が過去に使用したものと一致するかチェックしている • 一致した場合、flagsの更新をしていない = useEffectの動作と一致している // L. 1674
const prevDeps = prevEffect.deps; if (areHookInputsEqual(nextDeps, prevDeps)) { hook.memoizedState = pushEffect(hookFlags, create, destroy, nextDeps); return; } } } currentlyRenderingFiber.flags |= fiberFlags; // 副作用を適用するフラグを立てる
useEffectの実装を追ってみて • എܠʹ͋Δ࣮ͷҙਤ։ൃνʔϜͷߟ͑ํʹ;ΕΔ͜ͱ͕Ͱ͖ͨ • ReconcilerɺFiberͷಋೖཧ༝ͳͲ • ίʔυΛඈͼඈͼͰ͏ඞཁ͕͋Δ+FiberΛར༻ͨ͠ཧํ๏ʹෆ׳ΕͰί ʔυΛͬͯཧղ͢ΔͷΘΓͱେม • React
Hooksଞʹ࣮͞Ε͍ͯΔͷͰɺڵຯ͕͋ΔํͬͯΈͯʁ ◦ ࣗuseEffectͷ࣮͏ͷͰർΕ·ͨ͠...
参考 • React-Fiber-Architecture • Reconciliation - React • An Introduction
to React Fiber - The Algorithm Behind React • React Fiberとその周りについて調べた