1. 背景
Reactの比較的新しい機能であるフック(hooks)を用いると、以下のように関数コンポーネント(function component)の内部で副作用を扱うことが可能になる。
const Hello = () => {
useEffect(() => {
console.log("hello, effect!");
});
return <div>Hello, World!</div>
};
この内部でawait
をしたいとしよう(例えば、外部のAPIからデータを取得したい場合など)。
await
を実行する関数はasync
でなければならないので、単純に以下のように修正してみると、Warningが表示されてしまう。
const Hello = () => {
useEffect(async () => {
const data = await fetch("http://qiita.com/");
});
return <div>Hello, World!</div>
};
2. 解決策
useEffect()
に非同期関数を渡すのではなく、useEffect()
に渡される関数の中で非同期関数を定義し、呼び出すようにすれば良い。
const Hello = () => {
useEffect(() => {
const asyncFunc = async () => {
const data = await fetch("http://qiita.com/");
};
asyncFunc();
});
return <div>Hello, World!</div>
};
解決策に対応するカスタムフックの作成
さらに、以下のようなカスタムフックuseAsyncEffect()
を作成することで上記解決策のロジックを抽出できる。
export function useAsyncEffect(asyncFunc, deps) {
useEffect(() => {
(async () => {
asyncFunc();
})();
}, deps)
}
これを用いると、先述の解決策は以下のように簡単に書き換えることができる。
const Hello = () => {
useAsyncEffect(async () => {
const data = await fetch("http://qiita.com/");
});
return <div>Hello, World!</div>
};
3. 追記:クリーンアップを要する副作用の場合
上記のuseAsyncEffect()
ではクリーンアップを有する副作用を上手く扱うことができない。
通常のuseEffect()
のようにuseAsyncEffect()
内でクリーンアップ関数をreturnできるようにしたかったが、厳しそうなので、ひとまず以下のようにuseAsyncEffect()
の引数として渡す形をとることにした。
export function useAsyncEffect(asyncFunc, deps, cleanup=() => {}) {
useEffect(() => {
(async () => {
asyncFunc();
})();
return cleanup;
}, deps);
}
cleanupを渡すときのスコープが変わってしまうため、このままでは上手くいかないケースも考えられる...