kk-web

Api の呼び出しを hooks で囲うのはイマイチかも

2021-04-02

「Api の呼び出しはすべてグローバルな hooks から呼び出す」というルールが敷かれているプロジェクトをちょくちょく目にしますが。

そのせいでうまいこと実装できないケースに出会ったので、備忘録がてら書いていこうと思います。


まず Api の呼び出しを以下の通りとします。

type GetHogeParams = {
  fuga: string;
};

type GetHogeData = {
  piyo: number;
};

const getHoge = (params: GetHogeParams) =>
  axios.get<GetHogeData>("/api/hoge", {
    params,
  });

続いて getHoge を呼び出す hooks を切ります。

const getHogeHooks = (params: GetHogeParams) => {
  const [piyo, setPiyo] = useState<GetHogeData["piyo"] | undefined>();

  useEffect(() => {
    const callback = async () => {
      const {
        data: { piyo },
      } = await getHoge(params);

      setPiyo(piyo);
    };

    callback();
  }, [params]);

  return piyo;
};

最後に getHogeHooks を呼び出します。

const Moge: FC = () => {
  const piyo = getHogeHooks({ fuga: "fugafuga" });

  return typeof piyo === "undefined" ? null : <div>{piyo}</div>;
};

ぱっと見なにも問題なさそうですが、この書き方では例えば以下のようなケースは対応できません。

type MogeProps = {
  fuga?: string;
};

const Moge: FC<MogeProps> = ({ fuga }: MogeProps) => {
  // props.fuga は undefined を許容しているため渡せない…
  const piyo = getHogeHooks({ fuga });

  return typeof piyo === "undefined" ? null : <div>{piyo}</div>;
};

そのため、hooks はまだこう書いたほうがマシかなーと思いますが…。

const getHogeHooks = () => {
  const callback = useCallback((params: GetHogeParams) => getHoge(params), []);

  return callback;
};

個人的な意見として、そもそもグローバルな hooks のみ Api の呼び出しを許容する設計自体が必要ないんじゃないか?と思います。

なので普通にこう書けば良いのでは?とは思います。

type MogeProps = {
  fuga?: string;
};

const Moge: FC<MogeProps> = ({ fuga }: MogeProps) => {
  const [piyo, setPiyo] = useState<GetHogeData["piyo"] | undefined>();

  useEffect(() => {
    const callback = async () => {
      if (typeof fuga !== "string") {
        return;
      }

      const {
        data: { piyo },
      } = await getHoge({ fuga });

      setPiyo(piyo);
    };

    callback();
  }, [params]);

  return typeof piyo === "undefined" ? null : <div>{piyo}</div>;
};

もちろんケースバイケースだとは思いますが、意外とやってしまいそうな実装だったので備忘録がてら。

© 2018 kk-web