この記事は 2020年 Reactアドベントカレンダー 12日目の記事です。
はじめに
React の Functional Componentで副作用を実現するためのhooksのうち、おそらく最もわかりやすいuseStateがどのように実現されているかを調べた。
調べる
Reactのリリースをcheckout
Reactをgithubからcloneし、執筆時点で最新の v17.0.1 のtagをcheckout
$ git clone git@github.com:facebook/react.git
$ cd react
$ git checkout v17.0.1
useStateがexportされている部分を検索
VSCodeで開き、愚直に useState
で検索したところ、 React.js の 95行目 がexportしており、その実装は ReactHooks.js の 80行目 にあることがわかった。
ひたすら実装を探る
const dispatcher = ReactCurrentDispatcher.current
とあるので、とりあえず ReactCurrentDispatcher.current
が何かを調べる必要がある。
ReactCurrentDispatcher
の実装が↓
const ReactCurrentDispatcher = {
/**
* @internal
* @type {ReactComponent}
*/
current: (null: null | Dispatcher),
};
export default ReactCurrentDispatcher;
これだけなので、多分currentに値を突っ込んでいる部分があるはず、ということで ReactCurrentDispatcher.current =
で検索したところ、 7ファイルの中に112件も引っ掛かっていしまい早々と心が折れそうになる。
とはいえ、多分いろんなところから値がセットされるんだろうからstaticなDispatcherが取れれば良いんだろう、ということで見なかったことにして Dispatcher
が何なのかを調べたら、その中に useState
のインターフェースがあった。
じゃあDispatcherの実装を調べればそれで済む話になりそう、というわけでDispatcherの実装を調べる。
実装している部分ではDispatcherをimportしているはずなので、 type {Dispatcher
で検索したら、 ReactPartialRendererHooks.js
がimportしていて、 それが useState
やその他のhooksの実装をexportしていた。
やったぜ。
肝心のuseStateの実装を見ると、内部ではuseReducerが使われている ので、今度は useReducer
の実装を読む。
useState
から useReducer
に渡されているreducerが、 basicStateReducer
なので、 useState
は useReducer
の特別なもの、として実装されている様子。
useReducer
の中で isReRender
というフラグが参照されていて、おそらく再描画のときにはこのフラグが立っているんだろう。
とりあえず再描画でないほうの実装を読むと、 [この中でDispatchの実装が作られ、これが呼ばれると再描画が走るような作りになっているらしい。] (https://github.com/facebook/react/blob/v17.0.1/packages/react-dom/src/server/ReactPartialRendererHooks.js#L335)
再描画の仕組みは今回の調査の対象ではないので気にしない。
次に再描画のほうの実装を読むと、 値を memoizedState
にキャッシュし、更新された値とすでに作成済みのDispatchを返している様子。
Hooks自体がやっていることはここまでで、あとは Dispatch
で再描画を走らせる部分なのでここでおしまい。
調べてみて
少なくとも useState
と useReducer
は状態管理だけで大したことはやっていなかった。
大したことをやっているのは、おそらくDispatchで再描画する部分。
その部分も追々調べてみたい。
使うときは最初に宣言しておしまいだから1度作った参照が使い回されている気になっていたけど、描画されるたびに新しい参照が返されるんだな。そりゃそうか。
Singletonな状態管理がめっちゃたくさんあってバグらないようにするのは大変そうだがそのへんはライブラリあるあるなんだろうきっと。
終わりに
内部実装を見ると新しい気付きがあって面白い。
アドベントカレンダーに参加しなかったら絶対やらなかったことなので、参加してよかった。