useEffect Reactフックを使用するときに欠落している依存関係の警告を修正するにはどうすればよいですか?


178

React 16.8.6(以前のバージョン16.8.3では良かった)では、フェッチ要求で無限ループを回避しようとすると、このエラーが発生します

./src/components/BusinessesList.js
Line 51:  React Hook useEffect has a missing dependency: 'fetchBusinesses'.
Either include it or remove the dependency array  react-hooks/exhaustive-deps

私は無限ループを止める解決策を見つけることができませんでした。の使用を避けたいuseReducer()。私はこの議論を見つけましたhttps://github.com/facebook/react/issues/14920可能な解決策はYou can always // eslint-disable-next-line react-hooks/exhaustive-deps if you think you know what you're doing.私がやっていることに自信がないので、まだ実装しようとはしていません。

私はこの現在のセットアップを持っていますReactフックuseEffectは継続的に永久に/無限ループを実行し、唯一のコメントはuseCallback()私がよく知らないものについてです。

私が現在どのように使用しているかuseEffect()(これはと同様に最初に一度だけ実行したいcomponentDidMount()

useEffect(() => {
    fetchBusinesses();
  }, []);
const fetchBusinesses = () => {
    return fetch("theURL", {method: "GET"}
    )
      .then(res => normalizeResponseErrors(res))
      .then(res => {
        return res.json();
      })
      .then(rcvdBusinesses => {
        // some stuff
      })
      .catch(err => {
        // some error handling
      });
  };

回答:


191

fetchBusinessesメソッドをエフェクト以外の場所で使用していない場合は、単純にエフェクトに移動して警告を回避できます

useEffect(() => {
    const fetchBusinesses = () => {
       return fetch("theURL", {method: "GET"}
    )
      .then(res => normalizeResponseErrors(res))
      .then(res => {
        return res.json();
      })
      .then(rcvdBusinesses => {
        // some stuff
      })
      .catch(err => {
        // some error handling
      });
  };
  fetchBusinesses();
}, []);

ただし、レンダー以外でfetchBusinessesを使用している場合は、2つのことに注意する必要があります。

  1. 囲みクロージャーを使用してマウント中に使用した場合、メソッドとして渡さないという問題はありますfetchBusinessesか?
  2. あなたのメソッドは、それを囲むクロージャから受け取るいくつかの変数に依存していますか?これはあなたには当てはまりません。
  3. すべてのレンダリングで、fetchBusinessesが再作成されるため、useEffectに渡すと問題が発生します。したがって、依存関係配列に渡す場合は、最初にfetchBusinessesをメモする必要があります。

要約すると、fetchBusinesses外部でuseEffect使用している// eslint-disable-next-line react-hooks/exhaustive-deps場合は、ルールを無効にできます。そうでない場合は、useEffectの内部でメソッドを移動できます。

ルールを無効にするには、次のように記述します

useEffect(() => {
   // other code
   ...

   // eslint-disable-next-line react-hooks/exhaustive-deps
}, []) 

14
私はあなたがうまく説明した解決策を使いました。セットアップが異なるために使用した別のソリューションuseCallback()。ですから、例えば: const fetchBusinesses= useCallback(() => { ... }, [...])useEffect()、次のようになりますuseEffect(() => { fetchBusinesses(); }, [fetchBusinesses]);
ラス

1
@russ、あなたは正しい、それを依存配列に渡す場合は、useCallbackを使用してfetchBusinessをメモする必要があります
Shubham Khatri

eslint-disableステートメントをどこに置くかを示しておくとよいでしょう。useEffect
user210757の

1
// eslint-disable-next-line react-hooks/exhaustive-depsコードが正しいことをリンターに説明するために使用することは、ハックのようなものです。私は彼らがリンターをより賢くして引数が必須ではないことを検出する別の解決策を見つけることを期待しています
OlivierBoisséOct

1
@TapasAdhikary、そうです。useEffectに非同期関数を含めることができます。別の方法で書く必要があります。stackoverflow.com/questions/53332321/…を
Shubham Khatri

75
./src/components/BusinessesList.js
Line 51:  React Hook useEffect has a missing dependency: 'fetchBusinesses'.
Either include it or remove the dependency array  react-hooks/exhaustive-deps

JS / Reactエラーではなく、eslint(eslint-plugin-react-hooks)警告です。

これは、フックがfunction fetchBusinessesに依存していることを示しているので、依存関係として渡す必要があります。

useEffect(() => {
  fetchBusinesses();
}, [fetchBusinesses]);

関数が次のようにコンポーネントで宣言されている場合、レンダリングごとに関数が呼び出される可能性があります。

const Component = () => {
  /*...*/

  //new function declaration every render
  const fetchBusinesses = () => {
    fetch('/api/businesses/')
      .then(...)
  }

  useEffect(() => {
    fetchBusinesses();
  }, [fetchBusinesses]);

  /*...*/
}

関数が新しい参照で再宣言されるたびに

これを行う正しい方法は次のとおりです。

const Component = () => {
  /*...*/

  // keep function reference
  const fetchBusinesses = useCallback(() => {
    fetch('/api/businesses/')
      .then(...)
  }, [/* additional dependencies */]) 

  useEffect(() => {
    fetchBusinesses();
  }, [fetchBusinesses]);

  /*...*/
}

または単に関数を定義する useEffect

詳細:https : //github.com/facebook/react/issues/14920


14
これにより、新しいエラーが発生しますLine 20: The 'fetchBusinesses' function makes the dependencies of useEffect Hook (at line 51) change on every render. Move it inside the useEffect callback. Alternatively, wrap the 'fetchBusinesses' definition into its own useCallback() Hook
ロシア、

1
ソリューションは問題なく、関数で別の状態を変更する場合は、別の予期しない動作を回避するために依存関係を追加する必要があります
cesarlarsson

56

useEffectコールバックとして直接設定できます:

useEffect(fetchBusinesses, [])

それは一度だけトリガーするので、すべての関数の依存関係が正しく設定されていることを確認してください(を使用するのと同じcomponentDidMount/componentWillMount...


2020年2月21日の編集

完全を期すために:

1. useEffect(上記のように)関数をコールバックとして使用します。

useEffect(fetchBusinesses, [])

2.関数を内部で宣言する useEffect()

useEffect(() => {
  function fetchBusinesses() {
    ...
  }
  fetchBusinesses()
}, [])

3.でメモする useCallback()

この場合、関数に依存関係がある場合は、それらをuseCallback依存関係配列に含める必要がありますuseEffect。これにより、関数のパラメーターが変更された場合に再度トリガーされます。その上、それは多くの定型文です...したがって、関数をのuseEffectように直接渡すだけ1. useEffect(fetchBusinesses, [])です。

const fetchBusinesses = useCallback(() => {
  ...
}, [])
useEffect(() => {
  fetchBusinesses()
}, [fetchBusinesses])

4. eslintの警告を無効にする

useEffect(() => {
  fetchBusinesses()
}, []) // eslint-disable-line react-hooks/exhaustive-deps

2
私はあなたを愛しています...この答えはとても完全です!
Nick09

8

解決策はまた、反応によって与えられます、彼らuseCallbackはあなたがあなたの関数のメモ化バージョンを返すでしょうあなたが使うアドバイスをします:

'fetchBusinesses'関数は、レンダリングごとにuseEffect Hook(NN行目)の依存関係を変更します。これを修正するには、「fetchBusinesses」定義を独自のuseCallback()にラップします。フックreact-hooks / exhaustive-deps

useCallbackuseEffect違いはuseCallbackが関数を返すことと同じシグネチャを持っているため、使用は簡単です。次のようになります。

 const fetchBusinesses = useCallback( () => {
        return fetch("theURL", {method: "GET"}
    )
    .then(() => { /* some stuff */ })
    .catch(() => { /* some error handling */ })
  }, [/* deps */])
  // We have a first effect thant uses fetchBusinesses
  useEffect(() => {
    // do things and then fetchBusinesses
    fetchBusinesses(); 
  }, [fetchBusinesses]);
   // We can have many effect thant uses fetchBusinesses
  useEffect(() => {
    // do other things and then fetchBusinesses
    fetchBusinesses();
  }, [fetchBusinesses]);


1

この記事は、フックを使用してデータをフェッチするための優れた入門書です:https : //www.robinwieruch.de/react-hooks-fetch-data/

基本的に、内部にフェッチ関数の定義を含めますuseEffect

useEffect(() => {
  const fetchBusinesses = () => {
    return fetch("theUrl"...
      // ...your fetch implementation
    );
  }

  fetchBusinesses();
}, []);

1

2番目の引数タイプの配列を削除できます[]が、これfetchBusinesses()もすべての更新で呼び出されます。必要に応じて、IFステートメントをfetchBusinesses()実装に追加できます。

React.useEffect(() => {
  fetchBusinesses();
});

もう1つはfetchBusinesses()、コンポーネントの外部で関数を実装することです。依存関係の引数があるfetchBusinesses(dependency)場合は、呼び出しに渡すことを忘れないでください。

function fetchBusinesses (fetch) {
  return fetch("theURL", { method: "GET" })
    .then(res => normalizeResponseErrors(res))
    .then(res => res.json())
    .then(rcvdBusinesses => {
      // some stuff
    })
    .catch(err => {
      // some error handling
    });
}

function YourComponent (props) {
  const { fetch } = props;

  React.useEffect(() => {
    fetchBusinesses(fetch);
  }, [fetch]);

  // ...
}

0

実際、フックを使用して開発する場合、警告は非常に役立ちます。しかし、いくつかのケースでは、それはあなたを針刺すことができます。特に、依存関係の変更をリッスンする必要がない場合。

fetchBusinessesフックの依存関係の内部に置きたくない場合は、それを引数としてフックのコールバックに渡し、メインfetchBusinessesをデフォルト値として次のように設定できます

useEffect((fetchBusinesses = fetchBusinesses) => {
   fetchBusinesses();
}, []);

それはベストプラクティスではありませんではありが、場合によっては役立つことがあります。

また、Shubnamが書いたように、フックのチェックを無視するようESLintに指示するコードを以下に追加できます。

// eslint-disable-next-line react-hooks/exhaustive-deps

0

[ fetchBusinesses]を最初に一度だけ実行したいcomponentDidMount()

fetchBusinessesコンポーネントから完全に引き出すことができます:

const fetchBusinesses = () => { // or pass some additional input from component as args
  return fetch("theURL", { method: "GET" }).then(n => process(n));
};

const Comp = () => {
  React.useEffect(() => {
    fetchBusinesses().then(someVal => {
      // ... do something with someVal
    });
  }, []); // eslint warning solved!
  return <div>{state}</div>;
};

これは単純な解決策を提供するだけでなく、徹底的なデプス警告を解決します。Reactツリーの外部のモジュールスコープに常駐するfetchBusinessためComp、テストが改善され、容易になります。

古いクロージャースコープ(dep in )のために、コンポーネントから初期の小道具と状態fetchBusinessesを読み取ることしかできないため、ここでは外部の再配置がうまく機能します。[]useEffect

関数の依存関係を省略する方法

  • エフェクト内で関数を移動
  • コンポーネントの外に関数を移動-(これを使用しています)
  • レンダリング中に関数を呼び出しuseEffect、この値に依存させる(純粋な計算関数)
  • 関数を追加してdepを実行しuseCallback、最後の手段としてラップします

その他の解決策について:

fetchBusinesses内部のuseEffect()他の状態にアクセスする場合、内部を引っ張っても実際には効果がありません。eslintはまだ文句を言います:Codesandbox

また、eslintの徹底的なデプコメントを無視してコメントを無視します。依存関係のリファクタリングとオーバーホールを行うときに、それらを忘れるのは簡単です。


0
const [mount, setMount] = useState(false)
const fetchBusinesses = () => { 
   //function defination
}
useEffect(() => {
   if(!mount) {
      setMount(true);
      fetchBusinesses();
   }
},[fetchBusinesses]);

これは非常に簡単な解決策であり、es-lintの警告を上書きする必要はありません。コンポーネントがマウントされているかどうかを確認するフラグを維持するだけです。


0

あなたはこのようにしてみます

const fetchBusinesses = () => {
    return fetch("theURL", {method: "GET"}
    )
      .then(res => normalizeResponseErrors(res))
      .then(res => {
        return res.json();
      })
      .then(rcvdBusinesses => {
        // some stuff
      })
      .catch(err => {
        // some error handling
      });
  };

そして

useEffect(() => {
    fetchBusinesses();
  });

それはあなたのための仕事です。しかし、私の提案は、この方法も試してみることです。それは以前の方法よりも優れています。私はこのように使用します:

useEffect(() => {
        const fetchBusinesses = () => {
    return fetch("theURL", {method: "GET"}
    )
      .then(res => normalizeResponseErrors(res))
      .then(res => {
        return res.json();
      })
      .then(rcvdBusinesses => {
        // some stuff
      })
      .catch(err => {
        // some error handling
      });
  };
        fetchBusinesses();
      }, []);

特定のIDのベースでデータを取得する場合、コールバックにuseEffectを追加すると、[id]警告を表示できません React Hook useEffect has a missing dependency: 'any thing'. Either include it or remove the dependency array


-4

次の行のeslintを無効にするだけです。

useEffect(() => {
   fetchBusinesses();
// eslint-disable-next-line
}, []);

このようにして、コンポーネントがマウントしたのと同じように使用します(1回呼び出されます)

更新しました

または

const fetchBusinesses = useCallback(() => {
 // your logic in here
 }, [someDeps])

useEffect(() => {
   fetchBusinesses();
// no need to skip eslint warning
}, [fetchBusinesses]); 

someDepsが変更されるたびにfetchBusinessesが呼び出されます


無効にするのではなく、これを行うだけです:[fetchBusinesses]警告が自動的に削除され、問題が解決しました。
rotimi-best

7
@RotimiBest-これを行うと、質問に記載されているように無限の再レンダリングが発生します
user210757

私は実際にこの前に私のプロジェクトの1つでこの方法を実行しましたが、無限ループは生成されませんでした。もう一度チェックします。
rotimi-best

@ user210757待ってください。ただし、無限ループが発生するのはなぜですか。サーバーからデータを取得した後に状態を設定しているのとは異なります。状態を更新していた場合は、関数を呼び出す前にif条件を書き込むだけでuseEffect、状態が空かどうかを確認します。
rotimi-best

@ローティミ-最高でしahwile私はコメントが、私はあなたがuseEffect本体またはuseCallbackに移動しない限り機能は、同じように、常に再レンダリングされますので、毎回決して再作成されたと言うであろうから
user210757
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.