Open23
React Fast Refreshを調べる
ピン留めされたアイテム
タイトル通り、React Fast Refreshについて調べたことを追記して行きます💪
ある程度まとまったら、記事にする予定です📜
何かあれば、お気軽にコメントしてください💁♂️💁♀️
以下のリポジトリにある、ソースコードとドキュメントを読む。
以下の動画をある程度理解する。
動画を見て分かったこと
分かったことを以下に書いてきます...🖊
Fast Refreshとは?
- ソースコードの変更を素早く反映させるための機能。
- 動画では、React Nativeを用いて解説していた。
- バンドルを使用してローカル環境で作業する事を想定している
-
webpackやParcel、Metroなどのバンドラーを使用する
- Fast Refreshは、上記のバンドラーのプラグインを用いて実現している
- プラグインは更新されたコードを検知し、それがReactコンポーネントかどうかを判断する
- Reactコンポーネントであった場合、Reactが変更タイプ(種類)によってコンポーネントを更新する方法を決定する
- ランタイムの更新(コンパイル後の更新)は、Reactを使用して更新をスケジュール(予約)して、新しいコードが表示されるようにする
ホットリロードとの違いは?
- 多くの点で両者は似ている
- ではなぜ、Fast Refreshを作ったのか?
- React Nativeのホットリロードにはいくつかの問題点があった
- 関数コンポーネントをサポートして無かった( Hooksもサポート無し )
- 構文エラーやランタイムエラーが出てもリロードされない
- 変更後にリロードされないことが多い
- 変更するたびにパッチやコードの変換が必要( 結果、重くなりやすい )
- React Nativeのホットリロードにはいくつかの問題点があった
- Fast Refreshがホットリロードと違う所
- Fast Refreshの機能自体が、Reactに組み込まれている
- Hooks付きの関数コンポーネントをサポート
- リロードしてもStateを保持する
どのようにしてFast Refreshは実現されているか?
-
この動画では、Hooksの変更について解説する
-
用語について
- update(更新)
- Reactコンポーネントを再レンダリングし、State(状態)を維持する事
- remount(再マウント)
- 古いコンポーネントコードをアンマウントして、新しいバージョンのモノを再マウントする事
- update(更新)
-
コードが変更された時、Fast Refreshはコンポーネントをupdateするかremountするかを決定する
-
決定方法は、
components signature
と言うモノを用いて計算して決める -
components signature
はコンポーネントが使用するHooksを説明する文字列 -
例えば、
useState()
とuseMemo()
を使うコンポーネント場合は、以下のようなsignature
が含まれている可能性があるため、Fast Refreshによって計算される
動画より引用
// singnature: "useState{count} useMemo{val}"
function Example() {
const [count, setCount] = useState(0);
const val = useMemo(() => ..., [count]);
// ...
}
-
なぜこの
signature
文字列が役に立つのか?- コンポーネントの変更するたびにFast Refreshによって、この
signature
文字列を再計算します-
signature
が同じである場合、コンポーネントをupdate(更新)しても安全 -
signature
が変更された場合、コンポーネントをremount(再マウント)する
-
- コンポーネントの変更するたびにFast Refreshによって、この
-
例えば、
useState()
をuseReducer()
に置き換えるとsignature
が変更されます
動画より引用
- // signature: "useState{count}"
+ // signature: "useReducer{state}"
function Example() {
- const [count, setCount] = useState(0);
+ const [state, dispatch] = useReducer(...);
// ...
}
- この場合、Fast Refreshはマウントを解除してremount(再マウント)する
- Hooksの動作が異なっていてState(状態)を共有できないので、正しい挙動
Custom Hooksについて
- 内部の配列でHooksを管理しているため、fast refreshはCustom Hooksを認識する必要はありません。
- 例えば、ここに二つのCustom Hooksがfetchを使用しているとします。
動画より引用
function useFetch(url) {
const [value, setValue: = useState();
useEffect(() => ...);
// ...
}
function useSubscription(config) {
const [value, setValue] = useState();
useEffect(() => ...);
// ...
}
- どちらも組み込みの
useState()
とuseEffect()
を使用して、同じような反応をする。 - しかし、それを考慮して状態を共有するのは恐らく安全ではない
- そのため、fast refreshは作成する署名の一部としてCustom Hooks名を含めるようにしています。
- 補足: 署名にはCustom Hooks名が含まれるが、Custom Hooksを監視していない事に注意!
動画より引用
// signature: "useFetch{} useState{value} useEffect{}"
function useFetch(url) {
const [value, setValue: = useState();
useEffect(() => ...);
// ...
}
// signature: "useSubscription{} useState{value} useEffect{}"
function useSubscription(config) {
const [value, setValue] = useState();
useEffect(() => ...);
// ...
}
- 例えば、以下のようなコンポーネントから初めて、divの代わりにspanを返すように変更した場合、fast refreshでは更新時にHooksの状態が保持されますが、Custom Hooksを置き換えた場合には、変更された署名を確認して、新しいコンポーネントを新しい状態で再マウント(remount)します。
Hooksの状態が保持される例/動画より引用
// signature: "useFetch{value} useState{value} useEffect{}"
function Example() {
const value = useFetch(...);
- return <div>{value}</div>;
+ return <span>{value}</span>;
}
再マウント(remount)される例/動画より引用
- // signature: "useFetch{value} useState{value} useEffect{}"
+ // signature: "useSubscription{value} useState{value} useEffect{}"
function Example() {
- const value = useFetch(...);
+ const value = useSubscription(...);
return <span>{value}</span>;
}
- つまり、署名は
useFetch()
の状態が、useSubscription()
に影響してしまうのを防ぐのに役立ちます。
フックに保存しているものを変更するとどうなるか?
- fast refreshの署名には変数の名前が含まれている事に気づいたかもしれません。
- これは、変数を変更するとHooksの使用方法が変更されたことを意味するからです。
動画より引用
- // signature: "useState{isLoggedIn}"
+ // signature: "useState{isLoggedOut}"
function ExampleCmoponent() {
- const [isLoggedIn, setIsLoggedIn] = useState(false);
+ const [isLoggedOut, setIsLoggedOut] = useState(false);
// ...
}
Next.jsのFast Refreshも後で読む📖
ViteのFast Refresh Pluginも後で読む📖