1. daishi

    Posted

    daishi
Changes in title
+React.useEffectで非同期処理をする場合の注意点2つ
Changes in tags
Changes in body
Source | HTML | Preview
@@ -0,0 +1,88 @@
+## はじめに
+
+React 16.8から導入されたhooksにはuseEffectがあります。
+
+詳細は[公式サイト](https://ja.reactjs.org/docs/hooks-intro.html)をまず参照しましょう。
+
+useEffectを使うと、コンポーネントのレンダリングとは別に処理を書くことができます。useEffectでしばしば非同期処理を書くことがあります。例えば、サーバからのデータ取得の処理などがあります。
+
+以下では、useEffectで非同期処理を書く場合の注意点を2つ紹介します。ケースによっては注意点はこの2つだけではない可能性が高いので、ご留意ください。
+
+## promiseを返さない
+
+useEffectに渡す関数の戻り値はcleanup関数です。
+
+```javascript
+useEffect(() => {
+ console.log('side effect!');
+ const cleanup = () => {
+ console.log('cleanup!');
+ };
+ return cleanup;
+}, []);
+```
+
+cleanup関数は次のeffectが呼ばれる前やアンマウントする場合に呼ばれます。(depsが`[]`なのでこの例では後者のみ)
+
+よって、下記は間違いです。
+
+```javascript
+useEffect(async () => {
+ await new Promise(r => setTimeout(r, 1000));
+ console.log('side effect!');
+}, []);
+```
+
+このコードはcleanup関数の代わりにpromiseを返してしまっています。
+正しくは、下記のようにします。
+
+```javascript
+const sleep = ms => new Promise(r => setTimeout(r, ms));
+
+useEffect(() => {
+ const f = async () => {
+ await new Promise(r => setTimeout(r, 1000));
+ console.log('side effect!');
+ };
+ f();
+}, []);
+```
+
+## アンマウントのフラグを持つ
+
+非同期処理を書く場合、コンポーネントが削除された後にコールバックが呼ばれる場合があります。この時、コンポーネントのステートを変更しようとするとワーニングがでます。
+
+```javascript
+const [count, setCount] = useState(0);
+useEffect(() => {
+ const f = async () => {
+ await new Promise(r => setTimeout(r, 1000));
+ setCount(c => c + 1);
+ };
+ f();
+}, []);
+```
+
+これを回避するには次のようにアンマウントのフラグを持ちます。
+
+```javascript
+const [count, setCount] = useState(0);
+useEffect(() => {
+ let unmounted = false;
+ const f = async () => {
+ await new Promise(r => setTimeout(r, 1000));
+ if (!unmounted) {
+ setCount(c => c + 1);
+ }
+ };
+ f();
+ const cleanup = () => {
+ unmounted = true;
+ };
+ return cleanup;
+}, []);
+```
+
+## おわりに
+
+React HooksのuseEffectについて非同期処理を使う場合のよくあるケースの注意点について紹介しました。React Hooksはまだベストプラクティスが溜まっていないため、今後違う方法が主流になる可能性はある点についてはご注意ください。