最速攻略記事によると、Reactのuseはキャッシュと組み合わせる必要があらしい。
というわけでmoizeを使ってみたらいい感じだった。
"use client"; import { Suspense, use, useState } from "react"; import moize from "moize"; export default function Home() { return ( <main> <h1>Suspense</h1> <Suspense fallback={<div>Loading...</div>}> <Waiter /> </Suspense> </main> ); } const waiting: () => Promise<string> = moize( () => new Promise((resolve) => setTimeout(() => { resolve("hello world"); }, 500) ) ); const Waiter = () => { const [counter, updateCounter] = useState(0); const value = use(waiting()); return ( <> <button onClick={() => updateCounter((c) => c + 1)}>increment</button> <div>Count: {counter}</div> <div>Waited: {value}</div> </> ); };
動作確認
state更新の際にはuseに渡されているPromiseは評価されないので、レンダリングが部分的になっている。
waiting関数の評価は初回のみ。
ちなみにmoizeでキャッシュしないと、state更新のタイミングでuseに渡されたwaiting関数も毎回評価されるので再レンダリングされてしまう
しかし、moizeをただ単に使うだけだと、キャッシュの管理やinvalidationなどの機構が不十分なため実用性には欠ける。
そういう意味では巷にあるSuspenseサポート系のライブラリを使う方がよい。
追記
2023年8月時点ではReactのmainブランチにCache APIでラップされたfetchが実装されているため、将来的にはclient-side fetchでもmoizeなどのキャッシュライブラリなしでUse APIを楽に使える可能性がある。