【React】APIをカスタムフックで扱いやすくする

こんにちは Gaji-Labo フロントエンドエンジニアの茶木です。

apiとの格闘の日々が続いています。
特にエラーの取り回しは、非同期処理も重なっており、地味に複雑だと思います。
そこで、一度使いやすいパターンを作ってしまおうと思います。

基本形

エラー処理を書かない基本形は api の非同期処理を useEffect の中で吸収し、 res の有無で、ローディングとコンテンツを出し分けするようにします。

const [state, setState] = useState();
useEffect(() => {
  (async () => {
    const res = await getSomthingApi();
    setState(res);
  })();
}, []);

if(!res) return <p>ローディング中</p>
const {data} = res;
return <p>コンテンツ:{data.id}/{data.name}</p>

これにエラー処理を書きたいのですが、ここに trycatch を書くと少々複雑になりそうです。そこで下準備にカスタムフックを useApiLoader と useAsyncCallback 作ります。

useApiLoader

api のレスポンスを格納するための stateapi をセットで扱う useApiLoader です。基本形の1〜7行目をカスタムフックにした形です。

const useApiLoader = (fn, deps) => {
  const [state, setState] = useState();
  useEffect(() => {
    const f = async () => {
      const s = await fn();
      setState(s);
    };
    f();
  }, deps);
  return state;
};

useAsyncCallback

deps がついて少し便利な useAsyncCallback です。非同期版の useCallback のカスタムフックです。

const useAsyncCallback = (callback, deps) => 
  useCallback((...args) => {
    const f = async () => callback(...args);
    return f();
  }, deps);

apiをカスタムフックにする

この useAsyncCallback でラップした api のカスタムフック、例として useGetSomethingApi を作ります。
まずエラー処理のないケースで、レスポンスの加工をこの段階で行っています。今回は return res.data とし、後続の処理で使わない情報を捨てています。

const useGetSomethingApi = (id: string) =>
  useAsyncCallback(async (id) => {
    const res = await getSomethingApi(id);
    return res.data;
  }, [id]);

これにエラー処理を加えると以下のようになります。同じくcatch節 で エラー情報を加工しています。

const useGetSomethingApi = (id: string) =>
  useAsyncCallback(async () => {
    try {
      const res = await getSomethingApi(id);
      return res.data;
    } catch (e) {
      return {
        error: {
          code: e?.response?.data?.code,
          message: e?.response?.data?.message,
        }
      };
    }
  }, [id]);

使ってみる

カスタムフックにより、ローディング、エラー、正常表示の書きわけができるようになりました。

const getApi = useGetSomethingApi(id);
const data = useApiLoader(getApi, [getApi]);

if(!data) return <p>ローディング中</p>
if( "error" in data ) {
  const { error } = data;
  alert(`エラー:${error.code}/${error.message}`);
  return <></>;
}
return <p>コンテンツ:{data.id}/{data.name}</p>;

レンダリングパートは非同期処理であることを気にせずに記述できます。
しかも、もし id が変わると getApi が作り直され getSomethingApi が再度呼び出されるので react の振る舞いにも沿っていて安心ですね!

Gaji-Laboでは、React経験が豊富なフロントエンドエンジニアを募集しています

弊社ではReactの知見で事業作りに貢献したいフロントエンドエンジニアを募集しています。大きな制作会社や事業会社とはひと味もふた味も違うGaji-Laboを味わいに来ませんか?

もちろん、一緒にお仕事をしてくださるパートナーさんも随時募集中です。まずはお気軽に声をかけてください。お仕事お問い合わせや採用への応募、共に大歓迎です!

求人応募してみる!

投稿者 Chaki Hironori

webライターもやってるフロントエンドエンジニアです。Reactは自信があります。またデザイン畑の出身で、気持ちのいいアニメーションやインタラクティブな表現は丁寧に手掛けます。好きなものは中南米の遺跡で、スペイン語が少しできます。