ten986.net
天衣 甜茶 / ten986 の個人ブログです。フロントエンド中心の技術記事と思考整理。

React Docs BETA を読んだ気になる記事

2022-12-25
この記事は、「ten986 Advent Calendar 2022」の 22 日目の記事です。
https://qiita.com/advent-calendar/2022/ten986

React Docs BETA

React Docs BETA は、公式で記述されているドキュメントです。
将来的に、現在のドキュメントがリプレースされ、React Docs BETA が公式ドキュメントとなるそうです。
このサイトの内容は、以前You Might Not Need an Effectがバズったことで話題になりました。公式の出しているドキュメントということもあり、ザッと読んでみました。今回はその感想です。

Installation と Quick Start

必要な時に読めばいいだけなので省略

Describing the UI

React で Component を書くための基本事項が書かれていそう。
どうせ知ってる内容なのでスキップ

Adding Interactivity

State 管理の話。 大体 useState の話っぽい。
Render and Commit は読む価値ありますね。
どのような順序で screen update が走るかは知っておいていい。

Managing State

この辺から非常に重要な話が始まります。
「どのような状態管理をすべきか?」という話題について、各項目とも覚えておくべき概念を説明しています。

Choosing the State Structure

どのように状態を持つべきか?という話。
「x,y など関連の強い state は、同じ useState にグルーピングする」「fullname など冗長な state は持たない」といった内容を5つ挙げています。
「Avoid deeply nested state.」とかは知らなかったけど、複雑な状態になっているのであれば正規化するというのはReactを超えた共通思想っぽいですね。

Sharing State Between Components

コンポーネント同士で状態を共有するための話。
"lifting state up" は状態管理について考えたことがあれば触れる概念ではありますが、これが公式に記述されるのはいいですね。
「どのコンポーネントで状態を持てばよいか?」を考えるためには "lifting state up" を考えればよいので。
(なお、状態管理とコンポーネント構造を分離した概念が存在することは一旦無視します)
"single source of truth" も必須の話。

Preserving and resetting state

渡す key が変われば re-creating するよ、というのが本質っぽい。

Passing Data Deeply with Context

useContextuseReducer を使うのは状態管理の一手段でしかないとは思いますが、"Lifting state up" の結果起こりがちな "Prop drilling" に対処したいのはそうですね。

Escape Hatches

例のバズってた箇所です。
React から抜け出し、外部のシステムに繋ぐ手段として、 ref および useEffect を紹介しています。
この章は非常にボリュームが多いです。なぜなら React の外に出るため。

Referencing Values with Refs

ref の話です。
多くの人は ref は DOM 要素を取得するために覚えたと思いますが、それは ref の性質から生まれた1つの使用法でしかありません。
ref の本質は、コンポーネントに何かしらの情報を持たせたいものの、値の変更により「再レンダーをさせたくない」時に使う、というものです。
ref の変更により、再レンダーは起こりません。
例えば、setInterval の停止のために値を持っておくのは、ref で行います。
"ref vs state" についての記述もあります。
ref は "escape hatch" ですから、頻繁に使うものではありません。
この中に "state as a snapshot" へのリンクがあるのも面白いですね。
「レンダリングの際に ref.current を読み書きするな」というのは知らない情報でした。
レンダリングに必要なのなら state を使うべきですからね。

Manipulating the DOM with Refs

ref の話の続きです。DOM操作の話です。
ref={myRef} すると、commit 中に ref.current に DOM node を渡す、というのが本質かな。
あと、非破壊的な動作なら問題ないけど、破壊的な操作はリスクあるよ、っていうのはそれはそうか。

Synchronizing with Effects

useEffect の話です。
外部のシステムと繋がないといけなくなった時、例えば server connection とかとか。そういった時に使います。
React Component には「Rendering code」と「Event handlers」の2種のロジックがあるものの、それでは不十分なこともあります。
Effect は rendering process の後に起こる、というのも重要ですね。
useEffect 内で setCount して無限ループする例があるのもいい。
「dependency array を選ぶな」という主張をよくしてきますが、これ本当なんですかね。
業務コードでも普通に dependency array を lint 通りにしないことありますが・・・。
development では、useEffect は2回呼び出されます。
Component をそれぞれ1回ずつ remount をすることで、バグを発見しやすくするためです。
useEffect が2回呼ばれても正しく動くコードであるべき、という話になります。
2回呼ばれて非常に不都合になるものは useEffect に書いてはいけない、とも言えますね。
例えば商品の購入とかが useEffect にあってはいけない(商人の購入に限らず post がそう)。

You Might Not Need an Effect

例のバズってた箇所です。
非常に長い資料ですが、よくある useEffect の必要のないケースが具体例付きで書かれているので、一読すべきです。
「レンダリングのためのデータの変換は useEffect はいらない」「userEvent の handle に useEffect はいらない」の2パターンに大別できるとのこと。
コンポーネントに key を渡すと、keyが変わると setState がリセットされる、if 内で setState した方がマシ、chain of computations をするな・・・。などなど有用な知見がたくさん。
stale value での children の re-rendering を skip するのは、JSX が実は function で、それぞれが React の管理下にあるので制御できる、と考えたら自然なのかな?
案外「情報を親に持たせる」ことで解決する例もあったり、useSyncExternalStoreというマジで知らない Hooks が登場してたりします。

Lifecycle of Reactive Effects

useEffect の続きの話で、特にライフサイクルについてです。
ライフサイクルはそうなのですが、Effect は「reactive value に反応する」というのが一番いい主張ですね。
かつ、「何が reactive value なのか?」を考えさせるきっかけにもなります。

Separating Events from Effects

「何が reactive value なのか?」という話があったり。useEffectEvent の話があったり。
Under Construction が出てきた・・・。

Removing Effect Dependencies

「dependencies を変えたいなら、コードを変えろ」とか。

Reusing Logic with Custom Hooks

Custom Hooks の話ってここで出るんですね。へー。

今日の個人ブログ開発

最低限のStorybook導入をしました。
いやー、だるいんですよね、Storybookの導入。
実際の環境と違う環境で動くので、環境を合わせに行くところとかが特に・・・。
後に Visual Regression Test を導入するために、「ブログ記事ページの Story」を作ることにしました。
contentlayer を使って markdown を変換しているので、.contentlayer/generated からいい感じに変換されたものを持ってこれます。
あとはその「変換されたもの」をStoryにペタ。
これで「具体的なブログ記事に対する Story」ができました。めでたし。
特別やったこととしては、storybook-addon-next で環境を近づけたり、「プレビューに任意のcssを読み込ませる」方法を使ったり。
next/legacy/image が正しいサイズで表示されなかったので、素晴らしい issue commentを導入してなんとかしたりしました。