1. ホーム
  2. javascript

[解決済み] React Hook useEffectの非同期関数に関する警告:useEffect関数はクリーンアップ関数か何も返さないこと

2022-03-23 20:18:16

質問

を試していたのですが useEffect の例では、以下のような感じです。

useEffect(async () => {
    try {
        const response = await fetch(`https://www.reddit.com/r/${subreddit}.json`);
        const json = await response.json();
        setPosts(json.data.children.map(it => it.data));
    } catch (e) {
        console.error(e);
    }
}, []);

という警告がコンソールに表示されます。しかし、非同期呼び出しの場合、クリーンアップはオプションだと思います。なぜこの警告が出るのかよくわかりません。 例としてサンドボックスをリンクしています。 https://codesandbox.io/s/24rj871r0p

解決方法は?

を見ることをお勧めします。 Dan Abramov氏(Reactコアメンテナーの一人)の回答はこちらです。 :

<ブロッククオート

必要以上に複雑にしているのでは?

function Example() {
  const [data, dataSet] = useState<any>(null)

  useEffect(() => {
    async function fetchMyAPI() {
      let response = await fetch('api/data')
      response = await response.json()
      dataSet(response)
    }

    fetchMyAPI()
  }, [])

  return <div>{JSON.stringify(data)}</div>
}

長期的には、このパターンはレースコンディションを助長するため、お勧めしません。例えば、呼び出しが始まってから終わるまでの間に何かが起こるかもしれないし、新しいプロップを手に入れることができるかもしれません。その代わりに、データフェッチにはSuspensionを推奨します。

const response = MyAPIResource.read();

で、エフェクトなし。しかし、その間は非同期なものを別の関数に移動して、それを呼び出すことができます。

について詳しく書かれています。 実験的サスペンスはこちら .


eslintで外の関数を使いたい場合。

 function OutsideUsageExample({ userId }) {
  const [data, dataSet] = useState<any>(null)

  const fetchMyAPI = useCallback(async () => {
    let response = await fetch('api/data/' + userId)
    response = await response.json()
    dataSet(response)
  }, [userId]) // if userId changes, useEffect will run again

  useEffect(() => {
    fetchMyAPI()
  }, [fetchMyAPI])

  return (
    <div>
      <div>data: {JSON.stringify(data)}</div>
      <div>
        <button onClick={fetchMyAPI}>manual fetch</button>
      </div>
    </div>
  )
}


useCallbackを使用する場合、どのように動作するかの例をご覧ください。 使用コールバック . サンドボックス .

import React, { useState, useEffect, useCallback } from "react";

export default function App() {
  const [counter, setCounter] = useState(1);

  // if counter is changed, than fn will be updated with new counter value
  const fn = useCallback(() => {
    setCounter(counter + 1);
  }, [counter]);

  // if counter is changed, than fn will not be updated and counter will be always 1 inside fn
  /*const fnBad = useCallback(() => {
      setCounter(counter + 1);
    }, []);*/

  // if fn or counter is changed, than useEffect will rerun
  useEffect(() => {
    if (!(counter % 2)) return; // this will stop the loop if counter is not even

    fn();
  }, [fn, counter]);

  // this will be infinite loop because fn is always changing with new counter value
  /*useEffect(() => {
    fn();
  }, [fn]);*/

  return (
    <div>
      <div>Counter is {counter}</div>
      <button onClick={fn}>add +1 count</button>
    </div>
  );
}