Vue の watchEffect が React の useEffect の微妙な点を改善していていいなと思った
端的に言うと、cleanup用の処理の登録方法が違うおかげで、多くのパターンで書きたくなる非同期処理が記述しやすくなっている。
実際にコードを見たほうが早いと思うので、同一のコードを並べてみた。定義とかは雰囲気でお願いします。
code:vue.ts
const { fetcher, abort } = useAPI()
watchEffect(async (onCleanup) => {
onCleanup(() => abort())
try {
data.value = await fetcher(id.value)
} catch {}
})
code:react.ts
const { fetcher, abort } = useAPI()
useEffect(() => {
(async () => {
try {
setData(await fetcher(id))
} catch {}
})()
return () => abort()
大きく違うのは、effectの処理を記述する関数にasync関数を取れるかどうかと、cleanup処理の記述方法である。
多くのパターンでeffectの中ではAPIを飛ばしたいことがよくある。つまりasync関数を書きたいのだが、Reactではそれを許してくれない。なぜならeffectの戻り値でcleanup処理を記述するスタイルになっているため、Promiseで包むわけにはいかないからだ。Promiseが解決される前にcleanupを仕込んでおかないと意味がない。
ReactではuseEffectにただの関数しか渡せない。となると、しかたなくuseEffectの中でasyncの即時実行の関数を作るのだが、まあ書き味が良くない。たまに書く程度であればいいのだけれど、かなり多くのパターンでこうなってしまうと使いにくく感じてしまう。
VueのwatchEffectではその悩みを解決するかのようにcleanup処理を登録するための関数が、effectの引数に渡ってきてくれる。そのおかげで、effectの関数もasync関数にすることができる。書き味がよい。
---
こういう細かいデザイン、地味に好きだったりする。
APIのインターフェース設計もまた、デザインの一つ。どんなユーザーにどんな意味を届けるか、を想像しながら真心込めてインターフェースを書いていきたい。