6
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

HooksのuseStateがどのように実現されているか調べた話

Last updated at Posted at 2020-12-12

この記事は 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 なので、 useStateuseReducer の特別なもの、として実装されている様子。

useReducer の中で isReRender というフラグが参照されていて、おそらく再描画のときにはこのフラグが立っているんだろう。

とりあえず再描画でないほうの実装を読むと、 [この中でDispatchの実装が作られ、これが呼ばれると再描画が走るような作りになっているらしい。] (https://github.com/facebook/react/blob/v17.0.1/packages/react-dom/src/server/ReactPartialRendererHooks.js#L335)
再描画の仕組みは今回の調査の対象ではないので気にしない。

次に再描画のほうの実装を読むと、 値を memoizedState にキャッシュし、更新された値とすでに作成済みのDispatchを返している様子。

Hooks自体がやっていることはここまでで、あとは Dispatch で再描画を走らせる部分なのでここでおしまい。

調べてみて

少なくとも useStateuseReducer は状態管理だけで大したことはやっていなかった。
大したことをやっているのは、おそらくDispatchで再描画する部分。
その部分も追々調べてみたい。

使うときは最初に宣言しておしまいだから1度作った参照が使い回されている気になっていたけど、描画されるたびに新しい参照が返されるんだな。そりゃそうか。
Singletonな状態管理がめっちゃたくさんあってバグらないようにするのは大変そうだがそのへんはライブラリあるあるなんだろうきっと。

終わりに

内部実装を見ると新しい気付きがあって面白い。
アドベントカレンダーに参加しなかったら絶対やらなかったことなので、参加してよかった。

6
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?