12/9 に React conf 2021 が開催されました!!
React Suspense
やはり React 18 のメインコンテンツはSuspense
ですよね。
ついにSuspense
が使えるようになります...!!
React Suspenseとは?
今まで、非同期APIでデータフェッチした時に、下記のようなコードを書いたことはありませんか?
export const List = () => {
const [items, isLoading] = useData(...);
if (isLoading) return <Loading />;
return items.map(item => (
<li>{item.something}</li>
));
}
このコンポーネントでは、下記のようなことを行っています。
- データを取得する
- データがロード状態である時、Loadingコンポーネントの表示
- 取得したデータの表示
しかし、下記2つは同じ場所にいる必要がありません。
- データの取得
- ローディング状態の処理
理由は下記のように述べられています。
- データフェッチする時に、常にロード状態を返却する必要がある
- 任意のコンポーネント内でデータを追加/削除などの変更があった際に、その度にローディング状態をどうするか考えなくてはいけない
- ローディング状態どうするか問題がデータに紐付きすぎている
これらを解決するのがSuspense
です。
// 子コンポーネント(<List />)の準備ができているか検知し、できていなかったら<Loading />を表示する
export const Page = () => {
return (
<Suspense fallback={<Loading />} >
<List />
</Suspense>
)
}
// ここでローディングなのかを知る必要がない
export const List = () => {
const [item] = useData(...);
return items.map(item => (
<li>{item.something}</li>
));
}
もし、のchildren
が準備できていなかった場合、fallbackに定義されたコンポーネントが表示されます。
つまり、Suspense
では、「データが準備されたか」ではなく「JSXが準備されたか」という考え方に変わったのです。
そのため、データの依存関係やコンポーネントの依存関係を考えず、スケルトンやスピナーなどの準備状態のUIを表示することができるのです。
▼ 参考
Automatic batching
今まで下記のようにset関数を3つ書いた場合、3回再レンダーが走りました。
const handleClick = () => {
setIsFetching(false)
setError(null)
setFormStatus('success')
}
しかし、set関数を呼ばれる度にレンダリングするのではなく
関数の最後のみ実行されるようになります。
つまり、handleClick
は1回のみのレンダリングで済むようになります!
▼ 参考
React without memo
React Forgetという研究プロジェクションによる発表です。
これはまだ進行中のプロジェクトであり、まだリリースされていないものになります。
そもそもメモ化とはなんぞ?という人は、下記記事を参考にしてください。(告知)
メモ化することで、不要な再レンダリングを減らすことができるためユーザーには良い体験を与えられます。
一方で、データの依存関係や参照の同一性を考えてメモ化しなきゃいけないため、開発者は考えることが多い上にコード量も増えていきます。
UXとDX...この両方がよい状態にするにはどうすれば良いでしょう。
そこで、メモ化の方法を再検討したそうです。
大前提
useMemo
やuseCallback
は、props
やstate
の入力を受け取って、
UIやエフェクトなどに出力するための関数に過ぎません。
useMemoの再検討
const filtered = useMemo(
() => getFiltered(todos, visibility),
[todos, visibility]
)
useMemoがしていることは、キャッシュされている前回のレンダリングの値と比較して、
値に変更があったら再実行、値に変更がなかったらそのままにしているだけです。
let hasVisibilityChanged, hasTodosChanged, memoCache;
let filtered;
if (hasVisibilityChanged || hasTodosChanged) {
filtered = memoCache[1] = getFiltered(todos, visibility)
} else {
filtered = memoCache[1]
}
useCallbackの再検討
const handleChange = useCallback(
todo => setTodos((todos) => getUpdated(todos, todo)),
[]
)
useCallbackにおいても、前回の値がなかったら生成してキャッシュさせてあげるだけです。
const handleChange =
memoCache[0] ||
(memoCache[0] = (todo) => setTodos((todos) => getUpdated(todos, todo)));
このような仕組みを用いて、関数や値、さらにはjsx
などを自動的にメモライズしてくれる仕込みを
未来のReactに導入するみたいです!!
すでにでdemoがあります。
▼ demo映像からのリンク
期待大ですね!!この研究がうまくいくことを願っています。
これらはReact Confのほんの一部でしかありません!!
こちらに全動画がまとまっているので、ぜひチェックしてみてください