reactでAPIアクセスなどの非同期な処理を実施したい。
割と常識的な内容ですが、初心者の頃にうまくピンポイントな記事に巡り合う事ができなかったので書きました。
React hookでasync/await 非同期処理を実施する方法
非同期処理を実施するためには副作用フックと呼ばれるuseEffectを使います。
https://ja.reactjs.org/docs/hooks-effect.html
useEffectは第一引数に関数を受け取りその関数内で非同期な処理を実行することができます。
import { useEffect, useState } from 'react';
function ExampleComponent {
const [state, setState] = useState("");
useEffect(() => {
// useEffect自体ではasyncの関数を受け取れないので内部で関数を定義して呼び出す。
const access_db = async () => {
const response = await fetch("http://example.com/nanika.json");
const body = await response.json();
setState(body); // stateに反映する
};
access_db();
}, []);
// stateに応じた描画
return <pre>{state.toString()}</pre>
}
上記では第二引数に空配列を指定しているので、useEffectはコンポーネントが生きている間に一回のみ実行されるようになっています。
useEffectの第二引数を誤ると無限ループが発生する
公式の第二引数についての説明はなかなか抽象的で何を言っているのか分かりづらいと思います。
分かりやすく説明すると、useEffectの中身を再度実行するためのトリガーになる変数(stateやprops)を配列には入れておきます。
最初の例ではコンポーネントがレンダリングされるとき一回のみuseEffect内の処理が実施されてほしいので空配列を指定しています。
この引数の指定を間違えても一見うまく動いてるように見える事が多いのですが、内部ではレンダリングの無限ループが始まります。
最初の例のプログラムでは下記のような書き方をした場合無限ループに陥ります。
// JSXに使われているすべての変数(stateやprops)の変化に反応する
useEffect(() => {/*非同期処理*/})
// stateの全変化に反応する
useEffect(() => {/*非同期処理*/}, [state])
reactではstateの状態が変化した時に再度レンダリングが走るので…
初回レンダリング→遅れてuseEffect内の処理がステートを更新→ステートが状態変化したので再度useEffectの処理→ステートが状態変化したので再度useEffectの処理…
上記の様な無限ループに陥ります。
useEffectの第二引数にあえて指定する場合
第二引数にはあえてもう一度useEffect内の処理をもう一度実施したいときにそのトリガーになる変数(stateやprops)を指定します。
下記の例では、propsから送られてきたcountが変更された際に違うapiにアクセスさせたいので、useEffectの第二引数にcountを指定しています。
function ExampleComponent(props: {
count: number
}) {
const [state, setState] = useState("");
useEffect(() => {
// useEffectではasyncの関数を受け取れないので内部で関数を定義して呼び出す。
const access_db = async () => {
// countに応じてアクセスするAPIを変えなければならない
const url = "http://example.com/"+props.count.toString()+"/nanika.json";
const response = await fetch(url);
const body = await response.json();
setState(body); // stateに反映する
};
access_db();
}, [props.count]);
// stateに応じた描画
return (<pre>{state.toString()}</pre>);
}
countが更新されるときのみuseEffectが再実行されるようになります。