Closed5

RecoilのAtomでdefault: undefinedを回避したい

jintzjintz

Recoilでは、通常ステートを初期化する場合は以下のようにする。

const sampleState = atom<string | undefined>({
  key: 'sample',
  default: undefined,
  effects: [({ setSelf }) => setSelf('sample')],
});

const useSample = () => {
  const [sample, setSample] = useRecoilState(sampleState);
  // 各種処理
}

上記のような場合は特に処理がないから直接default: 'sample'のようにできる。
一方で、たとえばAPI経由で値を取得して初期値を格納したいなど、何らかの処理を噛ませたい場合はいったんdefault: undefinedとして後から値を格納するように記述されているのを多く見かける。

ただし、一度でもundefiendを許容してしまえば後々の全ての処理でundefiendを考慮する必要が出てくる。
毎回毎回分岐を考慮するのは非常に面倒なので、初期値から計算した値を入れたいけどどうしようかなという話。

……書いてて思ったけど、これselectorでいけるのでは?

jintzjintz

求める値が何らかの値に依存し、かつ常に求められる値であるならselectorで問題なさそう。
一方で、ログインユーザーのように状況によっては求められない値を格納したい場合、当然だけどその状況を想定した処理を考える必要がある。

値を求められない場合はエラーを投げてどこかの階層でハンドリングできたらうまくできそうだけど、そう簡単にいくかな……?
却って労力が増えそう。

jintzjintz
const hogeState = atom<string>({
  key: 'hoge',
  default: errorSelector('初期化してください。'),
});

const Fuga = () => {
  const setHoge = useSetRecoilState(hogeState);  // セッターのみ呼び出す
  const [viewHoge, setViewHoge] = useState(false);

  const onPress = () => {
    setHoge('hoge');
    setViewHoge(true);
  };

  return (
    <>
      <Button onPress={onPress} title="View Hoge" />
      {viewHoge && <ViewHoge />}
    </>
  );
};

const ViewHoge = () => {
  const hoge = useRecoilValue(hogeState);

  return <Text>{hoge}</Text>;
}

これでいける。
useSetRecoilState(hogeState)としているのは、値がerrorSelectorのまま呼び出すとエラーになるため。
同じようなやり方でSelectorも利用できることを確認した。

jintzjintz
const hogeState = atom<string | undefined>({
  key: 'hoge',
  default: undefined,
});

const fugaState = selector<string>({
  key: 'fuga',
  get: ({ get }) => {
    const hoge = get(hogeState);

    return typeof hoge === 'undefined'
      ? errorSelector('hogeが未定義です。')
      : hoge;
  }
})

const Fuga = () => {
  const [hoge, setHoge] = useRecoilState(hogeState);

  return (
    <>
      <Button onPress={() => setHoge('hoge')} title="Set Hoge" />
      {hoge && <ViewHoge />}
    </>
  );
};

const ViewHoge = () => {
  const hoge = useRecoilValue(fugaState);

  return <Text>{hoge}</Text>;
}

これでもいける。はず。

このスクラップは2022/07/30にクローズされました