以下の内容はhttps://let.blog.jp/tag/useMemoより取得しました。


useMemo と Promise
state から計算できる値で毎回計算したくないのを useMemo でメモ化していました
あるとき重めの処理の中に async 関数が入り await が必要になりました
レンダリング処理の途中に await は書けないです
await しないと useMemo の結果は Promise です

const promise = useMemo(() => {
return fn(foo, bar)
}, [fn, foo, bar])

こんなことを考えてみましたが React ではプロパティが途中で変わっても再レンダリングされなければ画面に影響しません

const value = useMemo(() => {
const result = {
value: null,
}
fn(foo, bar).then(result_value => {
result.value = result_value
})
return result
}, [fn, foo, bar])

resolve されたタイミングで再レンダリングさせるには state を更新するしかないです
setState するなら useEffect の中なのでこんな感じになります

useEffect(() => {
const promise = fn(foo, bar)
setState(null)
promise.then(result => {
setState(result)
})
}, [fn, foo, bar])

できれば避けたいやつです
毎回そういう事するのは避けたいのでフック化してこういう感じです

const usePromise = (promise) => {
const [value, setValue] = useState(null)
promise.then(result => setValue(result))
return value
}

const value = usePromise(promise)

ただこれだけだと promise が変わったときにリセットできないのでやはり useEffect は必要です
また何度も then で関数を設定することになるので 1 回だけで済むように useEffect の中で行うようにします

const usePromise = (promise) => {
const [value, setValue] = useState(null)
useEffect(() => {
setValue(null)
let alive = true
promise.then(result => {
alive && setValue(result)
})
return () => {
alive = false
}
}, [promise])
return value
}

const value = usePromise(promise)

引数の Promise は state に入れたり useMemo を使って同じ Promise を渡すようにしないと無限ループになります

const value = usePromise(Promise.resolve(1))

だと then の中で setValue(1) が実行されて再レンダリングされるたびに Promise が新しくなり また useEffect 内で then がセットされてのループです
ちょっとした使い方のミスで無限ループするのは扱いづらいのかもしれないです
useMemo をグローバルで共有したくなる
state から作れる値は state に保存せずレンダリング時に毎回作るわけですが 重くなる処理は毎回作り直したくないので useMemo を使って毎回計算しないようにします
ただ その state を複数のコンポーネントに渡してそれらのコンポーネントでも同じ計算結果を使いたいことがあります
state とそれから作った値をまとめて props で渡すこともできますが 渡す値を増やしたくはないです
state があれば作れるのですから 本来渡すべきデータは state だけのはずです

でもそうすると state とそれから作る値を使う全部のコンポーネントで個別に useMemo で再計算なんですよね
なのでグローバルな useMemo が欲しいというわけです

グローバルならフックに頼れなくなりますが モジュール内に共通の保存する場所を作れば済みます
同じ state を指定したら同じ値になるとすると 同じ state から別の計算によって値を作るケースに対応できないので それを見分けるキーが必要になります
同じ state とキーならどのコンポーネントからでも同じ結果を返します
同じ計算なら関数も同じになるはずなので 関数をキーにします

const data = []
const isSame = (a, b) =>
a.length === b.length &&
a.every((v, i) => v === b[i])
const globalMemo = (fn, deps) => {
const key = [fn, ...deps]
const item = data.find(x => isSame(x.key, key))
if (item) return item.value
const value = fn(...deps)
data.push({ key, value })
return value
}

これでいい感じに動きはするのですが data に古いデータがずっと残ってしまいます
保存を直近◯件に制限したり 保存したのが古くてもよく使うのは残すようにソートするなど工夫が必要そうです



以上の内容はhttps://let.blog.jp/tag/useMemoより取得しました。
このページはhttp://font.textar.tv/のウェブフォントを使用してます

不具合報告/要望等はこちらへお願いします。
モバイルやる夫Viewer Ver0.14