はじめに
RecoilのatomをパラメーターにしてselectorでAPIを送信してました。
useResetRecoilState
でリセットしてatomの値を更新してもselectorがAPIを送信しないのが今回の問題です。
今回は 「ボタンを押すとデフォルト値に戻してAPIを送信する」 という動作を前提にしています
サンプルコード
// defaultFuncには何かしらのデフォルト値が設定されます
export const atomValue = atom({
key: "atomValue",
default: defaultFunc,
})
// atomをパラメーターにとり何かしらのAPIを送信する。
export const apiSelector = selector({
key: "apiSelector",
get: async ({ get }) => {
const atomParam = get(atomValue)
const respnse = await getApi(atomParam)
return respnse
}
})
export const sample = () => {
const resetAtomValue = useResetRecoilState(atomValue)
return (
<Button
onClick={resetAtomValue}
>
リセット
</Button>
}
)
色々と試してみた
-
ボタンを押した時に
useResetRecoilState
を使用するのではなく、直接デフォルト値を設定する -
ログを仕込んでatomボタンがが更新されているか確認する
-
useEffectを使用していたので、useEffectの副作用を疑って、できるだけレンダリングしないようにuseEffectを解体
-
reset関数を使用してリセットを行っているが、reset関数内の更新でatomが競合している可能性があるため、useRecoilCallBackを使用してみる
その後、selectorにログを仕込んで確認したところ、atomが更新されているのにも関わらずにselectorが実行されていないということがわかりました。
通常、selector内でgetしているatomが変更されるとAPIが送信されるはずです。
selector内ではしっかりとgetしています。const atomParam = get(atomValue)
解決策
RequestseIDをselector内のsetに用意して、リセットボタンを押下するたびにset内の処理も実行されるように変更しました。
そうすることで、強制的に更新させてAPIを送信するように修正しました。
処理の流れは以下になります
・リセットボタンを押下する
・resetAtomValue()
でatomがデフォルトの値にリセットされる
・ apiSelector()
でsetが更新されてAPIが送信される
// RequestseIDを管理するatomを作成
export const apiSelectorRequestId = atom<number>({
key: "apiSelectorRequestId",
default: 0,
})
export const apiSelector = selector({
key: "apiSelector",
get: async ({ get }) => {
const atomParam = get(atomValue)
const respnse = await getApi(atomParam)
return respnse
},
// setを使用してatomを強制的に更新させる
set: ({ set }) => {
set(apiSelectorRequestId, (n) => n + 1)
},
})
export const sample = () => {
const resetAtomValue = useResetRecoilState(atomValue)
const resetApiSelectorRequestId = useResetRecoilState(apiSelector)
return (
// resetAtomValue();でデフォルト値に戻してからapiSelector();でRequestseIDを更新することでAPIを送信
<Button
onClick={() => {
resetAtomValue();
apiSelector();
}}
>
リセット
</Button>
}
)