React: [ESLint] 'exhaustive-deps'lintルールのフィードバック

作成日 2019ĺš´02月21日  Âˇ  111コメント  Âˇ  ソース: facebook/react

よくある回答

💡💡💡💡💡💡💡💡💡💡💡💡💡💡💡💡💡💡💡💡💡💡

この投稿へのコメントを分析して、ガイダンスを提供しました: https ://github.com/facebook/react/issues/14920#issuecomment-471070149。

💡💡💡💡💡💡💡💡💡💡💡💡💡💡💡💡💡💡💡💡💡💡


これは何ですか

これは、 useEffectなどのフックの依存関係のリストを検証する新しいESLintルールであり、古いクロージャの落とし穴から保護します。 ほとんどの場合、自動修正があります。 今後数週間でドキュメントを追加する予定です。

autofix demo

インストール

yarn add eslint-plugin-react-hooks<strong i="18">@next</strong>
# or
npm install eslint-plugin-react-hooks<strong i="19">@next</strong>

ESLint構成:

{
  "plugins": ["react-hooks"],
  // ...
  "rules": {
    "react-hooks/rules-of-hooks": 'error',
    "react-hooks/exhaustive-deps": 'warn' // <--- THIS IS THE NEW RULE
  }
}

ルールが機能することを確認するための簡単なテストケース:

function Foo(props) {
  useEffect(() => {
    console.log(props.name);
  }, []); // <-- should error and offer autofix to [props.name]
}

lintルールは文句を言いますが、私のコードは問題ありません!

この新しいreact-hooks/exhaustive-deps lintルールが適用されても、コードが正しいと思われる場合は、この号に投稿してください。


コメントを投稿する前に

次の3つを含めてください。

  1. 意図を表現する最小限のコード例を示すCodeSandbox(「foobar」ではなく、実装している実際のUIパターン)。
  2. ユーザーが実行する
  3. フック/コンポーネントの目的のAPIの説明。

please

しかし、私の場合は単純です、私はそれらのものを含めたくありません!

それはあなたにとっては簡単かもしれませんが、私たちにとってはまったく簡単ではありません。 コメントにどちらも含まれていない場合(CodeSandboxリンクがない場合など)、それ以外の場合はディスカッションを追跡するのが非常に難しいため

このスレッドの最終目標は、一般的なシナリオを見つけて、それらをより優れたドキュメントと警告に変換することです。 これは、十分な詳細が利用可能な場合にのみ発生する可能性があります。 不完全なコードスニペットを含むドライブバイコメントは、ディスカッションの質を大幅に低下させます—それだけの価値はありません。

ESLint Rules Discussion

最も参考になるコメント

今日は@threepointoneでこれらをパスしました。 要約は次のとおりです。

Lintルールで修正されました

無関係なuseEffect依存関係

正当なシナリオがあるため、このルールは、 useEffect 「外部」のdepを追加することを妨げるものではありません。

同じコンポーネント内で機能しますが、エフェクトの外部で定義されます

Linterは、現在安全である場合には警告しませんが、それ以外の場合はすべて、より良い提案を提供します(関数をエフェクト内に移動したり、 useCallbackラップしたりするなど)。

ユーザーコードで修正する価値がある

小道具の状態をリセットすると変化します

これでlint違反は発生しなくなりましたが、小道具に応じて状態をリセットする慣用的な方法は異なります。 このソリューションでは、レンダリングに一貫性がないため、望ましいかどうかはわかりません。

「私の非関数値は一定です」

フックは、可能な限り正確に向かってあなたを少しずつ動かします。 あなたは(いくつかのケースでは、あなたが省略できます)DEPSを指定した場合、私たちは強く、あなたが変更されないと思うことも、ものを含めることをお勧めします。 はい、このuseDebounce例では、遅延が変わる可能性は

特定の値が静的であると絶対に主張する場合は、それを強制することができます。
最も安全な方法は、APIで明示的に行うことです。

const useFetch = createFetch({ /* config object */});
const useDebounce = createDebounce(500);
const FormInput = createInput({ rules: [emailValidator, phoneValidator] });

次に、レンダリング内に配置しない限り、明らかに変更することはできません。 (これはフックの慣用的な使用法ではありません。)しかし、 <Slider min={50} />は決して変更できないと言うことは、実際には有効ではありません。誰かが簡単に<Slider min={state ? 50 : 100} />に変更する可能性があります。 実際、誰かがこれを行うことができます:

let slider
if (isCelsius) {
  slider = <Slider min={0} max={100} />
} else {
  slider = <Slider min={32} max={212} />
}

誰かがisCelsius状態を切り替えた場合、 minが変更されないと想定しているコンポーネントは更新に失敗します。 この場合、 Sliderが同じものになるかどうかは明らかではありません(ただし、ツリー内で同じ位置にあるためです)。 したがって、これはコードに変更を加えるという点で主要な足がかりです。 Reactの主なポイントは、更新が初期状態と同じようにレンダリングされることです(通常、どちらがどちらであるかはわかりません)。 小道具の値Bをレンダリングするか、小道具の値AからBに移動するかにかかわらず、外観と動作は同じである必要があります。

これはお勧めできませんが、場合によっては、強制メカニズムは、値が変更されたときに警告するフックである可能性があります(ただし、最初のメカニズムを提供します)。 少なくともその場合は、気付かれる可能性が高くなります。

function useMyHook(a) {
  const initialA = usePossiblyStaleValue(a);
  // ...
}

function usePossiblyStaleValue(value) {
  const ref = useRef(value);

  if (process.env.NODE_ENV !== 'production') {
    if (ref.current !== value) { // Or your custom comparison logic
      console.error(
        'Unlike normally in React, it is not supported ' +
        'to pass dynamic values to useMyHook(). Sorry!'
      );
    }
  }

  return ref.current;
}

更新を処理できないという正当なケースもあるかもしれkeyをリセットするラッパーコンポーネントを作成して、新しい小道具でクリーンな再マウントを強制することもできます。 これは、スライダーやチェックボックスなどのリーフコンポーネントにおそらく適しています。

「私の関数値は一定です」

まず第一に、それが一定でトップレベルのスコープに引き上げられている場合、リンターは文句を言いません。 しかし、それは小道具や文脈から来るものには役立ちません。

それが真に一定であれば、DEPSでそれを指定すると、傷つけることはありません。 カスタムフック内のsetState関数がコンポーネントに返され、エフェクトから呼び出す場合などです。 lintルールは、このような間接参照を理解するのに十分なほど賢くはありません。 しかし一方で、誰でも後で戻る前にそのコールバックをラップし、おそらくその中の別の小道具や状態を参照することができます。 そうすると、それは一定ではなくなります! そして、これらの変更を処理できないと、厄介な古い小道具/状態のバグが発生します。 したがって、それを指定する方が適切なデフォルトです。

ただし、関数値が必然的に一定であるというのは誤解です。 メソッドのバインドにより、クラス内で一定であることがよくありますが、それによって独自の範囲のバグが発生します。 ただし、一般に、関数コンポーネントの値を閉じる関数は、定数とは見なされません。 lintルールは、何をすべきかを指示することについてより賢くなりました。 (エフェクト内に移動する(最も簡単な修正)、またはuseCallbackラップするなど。)

これの反対のスペクトルに問題があります。これは、無限ループが発生する場所です(関数値は常に変化します)。 可能な場合は(同じコンポーネントで)lintルールでそれをキャッチし、修正を提案します。 しかし、何かを数レベル下に渡すと注意が必要です。

それでも問題を修正するためにuseCallbackラップすることができます。 技術的には、関数を変更することは有効であり、バグのリスクを冒さずにこのケースを無視することはできないonChange={shouldHandle ? handleChange : null}や、同じ場所でのfoo ? <Page fetch={fetchComments /> : <Page fetch={fetchArticles />レンダリングなど。 または、親コンポーネントの状態を閉じるfetchCommentsですらあります。 それは変わるuseCallbackと、関数のID自体が変更されますが、必要な場合に限ります。 したがって、これは便利なプロパティであり、回避するための障害ではありません。

無限の非同期ループを検出するためのより良いソリューションを追加する必要があります。 これにより、最も紛らわしい側面が緩和されるはずです。 将来、これに対する検出を追加する可能性があります。 次のようなものを自分で書くこともできます。

useWarnAboutTooFrequentChanges([deps]);

これは理想的ではなく、これをより優雅に処理することを検討する必要があります。 私はこのようなケースがかなり厄介であることに同意します。 ルールに違反しない修正は、たとえばAPIをcreateTextInput(rules)に変更して、 rules静的にし、 registerとunregisterをuseCallbackにラップすることです。 registerとunregister削除し、 dispatchだけを配置する別のコンテキストに置き換えます。 そうすれば、それを読むこととは異なる関数IDを持つことが決してないことを保証できます。

とにかくコンテキスト値にuseMemoを追加することをお勧めします。これは、プロバイダーが多くの計算を行うため、新しいコンポーネントが登録されていないが、自身の親が更新された場合に繰り返すのは悲しいことです。 したがって、この種の問題は、他の方法では気付かなかったかもしれない問題をより目立たせます。 私は同意しますが、これが起こったときにそれをさらに目立たせる必要があります。

関数従属性を完全に無視すると、関数コンポーネントとフックのバグが悪化します。これを行うと、古い小道具と状態が表示され続けるためです。 ですから、できる限りそうしないようにしてください。

複合値の変化に反応する

この例が本質的にイベントハンドラーである何かにエフェクトを使用する理由は私には奇妙です。 イベントハンドラーで同じ「ログ」(フォーム送信の可能性があると思います)を実行する方が適切なようです。 これは、コンポーネントがアンマウントされたときに何が起こるかを考える場合に特に当てはまります。 エフェクトがスケジュールされた直後にアンマウントした場合はどうなりますか? その場合、フォーム送信のようなものは単に「起こらない」べきではありません。 ですから、効果は間違った選択かもしれないようです。

つまり、 fullNameをsetSubmittedData({firstName, lastName})にすると、 [submittedData]が依存関係になり、そこからfirstName読み取ることができます。 lastName 。

命令型/レガシーコードとの統合

jQueryプラグインや生のDOMAPIなどの必須のものと統合する場合、多少の不快感が予想される場合があります。 そうは言っても、その例では、効果をもう少し統合できると期待しています。


誰も忘れなかったといいのですが! 私がやったのか、何か不明な点があるのか​​教えてください。 これからのレッスンをすぐにいくつかのドキュメントに変えようとします。

全てのコメント111件

この例には返信があります: https :

CodeSandbox

これは制御されていないチェックボックスコンポーネントであり、 defaultIndeterminate小道具を使用して初期レンダリングで不確定なステータスを設定します( indeterminate要素属性がないため、参照を使用してJSでのみ実行できます)。 この小道具はdefaultValueように動作することを目的としており、その値は最初のレンダリングでのみ使用されます。

このルールは、依存関係の配列にdefaultIndeterminateがないことを訴えますが、それを追加すると、新しい値が渡された場合にコンポーネントが制御されていない状態を誤って上書きする原因になります。 依存関係の配列を完全に削除することはできません。これは、不確定なステータスが小道具によって完全に制御される原因となるためです。

これと、ルールがキャッチするのが_意味がある_ケースのタイプを区別する方法はわかりませんが、最終的なルールのドキュメントに推奨される回避策が含まれていると便利です。 🙂

Re: https :

@billyjanitsch代わりにこれは機能しますか? https://codesandbox.io/s/jpx1pmy7ry

defaultIndeterminate初期化されるindeterminate useStateを追加しました。 次に、エフェクトは[indeterminate]を引数として受け入れます。 現在は変更していませんが、後で変更した場合は、それでも機能すると思いますか? したがって、コードは将来の可能なユースケースをもう少し良く予測します。

だから私はいくつかのhtmlを渡し、それをdangerouslySetInnerHtmlと一緒に使用してコンポーネント(いくつかの編集コンテンツ)を更新する次の(エッジ?)ケースを取得しました。
私はhtml小道具を使用していませんが、 ref.current.querySelectorAllを使用して魔法をかけることができる参照を使用しています。
明示的に使用していなくても、 useEffect依存関係にhtmlを追加する必要があります。 これは、実際にルールを無効にする必要があるユースケースですか?

アイデアは、編集コンテンツからのすべてのリンククリックを傍受し、いくつかの分析を追跡するか、他の関連することを行うことです。

これは、実際のコンポーネントからの骨抜きの例です。
https://codesandbox.io/s/8njp0pm8v2

私はreact-reduxを使用しているので、小道具でアクションクリエーターをmapDispatchToPropsから渡し、そのアクションクリエーターをフックで使用すると、 exhaustive-deps警告が表示されます。

だから私は明らかにreduxアクションを依存関係配列に追加することができますが、reduxアクションは関数であり決して変更されないので、これは不必要ですよね?

const onSubmit = React.useCallback(
  () => {
    props.onSubmit(emails);
  },
  [emails, props]
);

lintがdepsを[emails, props.onSubmit]に修正することを期待していますが、現在は常にdepsを[emails, props]修正しています。

  1. 意図を表現する最小限のコード例を示すCodeSandbox(「foobar」ではなく、実装している実際のUIパターン)。

https://codesandbox.io/s/xpr69pllmz

  1. ユーザーが実行する

ユーザーはメールを追加し、それらのメールをアプリに招待します。 UIの残りの部分は、私の問題とは無関係であるため、意図的にbuttonだけに削除します。

  1. フック/コンポーネントの目的のAPIの説明。

私のコンポーネントには、 onSubmit: (emails: string[]) => voidという単一の小道具があります。 ユーザーがフォームを送信すると、 emails状態で呼び出されます。


編集: https: //github.com/facebook/react/issues/14920#issuecomment-467494468で回答

これは、技術的にはprops.foo()がprops自体をthisからfoo呼び出しとして渡すためです。 したがって、 fooは暗黙的にprops依存する可能性があります。 ただし、この場合はもっと良いメッセージが必要です。 ベストプラクティスは常に破壊することです。

CodeSandbox

サードパーティのライブラリを統合するときに、マウントと更新が明確に異なる可能性があることは考慮されていません。 インスタンスはすべてのレンダリングで破棄されるべきではないため、更新効果をマウント1に含めること(および配列を完全に削除すること)はできません。

やあ、

ここで私のコードの何が問題になっているのかわからない:

const [client, setClient] = useState(0);

useEffect(() => {
    getClient().then(client => setClient(client));
  }, ['client']);

私はReact Hook useEffect has a complex expression in the dependency array. Extract it to a separate variable so it can be statically checkedを手に入れました

@joelmoss @sylvainbaronnetフィードバックに感謝しますが、問題の冒頭で求めた情報が含まれていなかったため、コメントを非表示にしました。 そのため、コンテキストが欠落しているため、この議論は誰にとっても不必要に困難になります。 もう一度投稿して、関連するすべての情報を含めていただければ、会話を続けさせていただきます(一番上の投稿を参照)。 理解していただきありがとうございます。

  1. CodeSandbox
  2. ユーザーは名を選択でき、名前を強制的に変更します。 ユーザーは姓を選択でき、名を強制的に変更することはありません。
  3. 状態の一部を返すカスタムフック、その状態を更新するselectコンポーネント、およびプログラムで状態を更新する状態フックのupdateメソッドがあります。 示されているように、私は常にアップデータ関数を使用したくないので、返された配列の最後の項目に残しました。

現状のコードはエラーにならないはずだと思います。 現在、35行目で、 setLastNameを配列に含める必要があると言っています。 それについて何をすべきかについて何か考えはありますか? 私は予期しないことをしていますか?

私は完全に理解しており、通常はすべてをあなたに代わって行いますが、私の特定のケースでは、どのコードも私に固有のものではありません。 フックの外部で定義され(つまり、reduxアクションクリエーター)、フック内で使用される関数を使用するには、その関数をフックdepとして追加する必要があるかどうかについての質問です。

さらに情報が必要な場合は、コードサンドボックスを作成してください。 THX

@joelmoss私のリプロもあなたのケースをカバーしていると思います。

はい、CodeSandboxは引き続き役立ちます。 人々のコードスニペットを一日中コンテキストスイッチするのがどのようなものか想像してみてください。 それは大きな精神的犠牲です。 それらのどれも同じに見えません。 人々がReduxやReactの外部にある他の概念でアクションクリエーターをどのように使用しているかを覚えておく必要がある場合、それはさらに困難です。 問題はあなたには明白に聞こえるかもしれませんが、あなたが何を意味したのかは私にはまったく明白ではありません。

@gaearon私はそれが理にかなっていることを理解しています、私が達成したかった理由は:

エフェクトを実行して1回だけクリーンアップする場合(マウント時とアンマウント時)、2番目の引数として空の配列([])を渡すことができます。

説明してくれてありがとう。 (そしてオフトピックで申し訳ありません)

これが私が実装しているもののコードバージョンです。 あまり見えませんが、パターンは私の実際のコードと100%同じです。

https://codesandbox.io/s/2x4q9rzwmp?fontsize=14

  • ユーザーが実行する

この例ではすべてがうまく機能します! リンターの問題を除いて。

  • フック/コンポーネントの目的のAPIの説明。

このようなファイルがいくつかあり、エフェクトで関数を実行していますが、シリーズIDの変更など、何らかの条件が満たされた場合にのみ実行したいと考えています。 関数を配列に含めたくありません。

これは、サンドボックスコードで実行したときにリンターから取得したものです。

25:5   warning  React Hook useEffect has a missing dependency: 'fetchPodcastsFn'. Either include it or remove the dependency array  react-hooks/exhaustive-deps

私の質問は次のとおりです:これはどのように動作する必要がありますか(リンタールールまたはフック配列ルールのいずれか)? この効果を説明するより慣用的な方法はありますか?

@svenanders 、 fetchPodcastsFnを含めたくない理由が気になりますか? レンダリングごとに変化するからですか? そうである場合は、その関数をメモ化するか、静的にします(パラメーターがない場合)。

一つには、それは明快さについてです。 関数を見ると、いつ起動するかを簡単に理解したいと思います。 配列に_one_idが表示されている場合、それは非常に明確です。 そのIDと一連の関数を見ると、さらに混乱します。 何が起こっているのかを理解するために、時間と労力を費やさなければならず、場合によってはデバッグ機能さえも費やさなければなりません。
私の関数は実行時に変更されないので、それらをメモ化することが重要かどうかはわかりません(これは特に、エピックを起動して最終的に状態を変更するディスパッチアクションです)。

https://codesandbox.io/s/4xym4yn9kx

  • ステップ

ユーザーはページ上のルートにアクセスしますが、スーパーユーザーではないため、ページからリダイレクトします。 props.navigateはルーターライブラリを介して注入されるため、ページの再読み込みを防ぐために実際にはwindow.location.assignを使用したくありません。

すべてが機能します!

  • 寞蹥ぎAPI

コードサンドボックスのように依存関係を正しく入力しましたが、リンターは依存関係リストにprops.navigateではなくpropsが必要であると言っています。 何故ですか?

スクリーンショット付きのツイート! https://twitter.com/ferdaber/status/1098966716187582464

編集:これがバグである可能性がある1つの正当な理由は、 navigate()がバインドされたthisに依存するメソッドである場合です。この場合、技術的には、 props内の何かが変更された場合、内部のものが変更されます。 thisも変更されます。

CodeSandbox: https ://codesandbox.io/s/711r1zmq50

対象のAPI:

このフックを使用すると、急速に変化する値をデバウンスできます。 デバウンスされた値は、useDebounceフックが指定された期間呼び出されなかった場合にのみ、最新の値を反映します。 レシピで行っているように、useEffectと組み合わせて使用​​すると、API呼び出しなどのコストのかかる操作が頻繁に実行されないようにすることが簡単にできます。

手順:

この例では、Marvel Comic APIを検索し、useDebounceを使用して、キーストロークごとにAPI呼び出しが発生しないようにします。

依存関係配列で使用する「すべて」を追加するIMOは効率的ではありません。

たとえば、 useDebounceフックについて考えてdelayは最初のレンダリング後に変更されませんが、すべての再レンダリングで変更されているかどうかを確認しています。 したがって、このフックでは、 useEffectの2番目の引数は、 [value, delay]ではなく[value]ある方が適切です。

浅い等式チェックが極端に安いとは思わないでください。 戦略的に配置するとアプリを支援できますが、すべてのコンポーネントを純粋にするだけで、実際にはアプリの速度が低下する可能性があります。 トレードオフ。

依存関係の配列にすべてを追加すると、どこでも純粋なコンポーネントを使用するのと同じ(またはさらに悪い)パフォーマンスの問題があると思います。 使用している値の一部が変更されないことがわかっているシナリオが多数あるため、 @ gaearonが述べたように、浅い等価性チェックはそれほど安価ではなく、これが可能であるため、依存関係配列にそれらを追加しないでください。アプリを遅くします。

慣例により、このルールがカスタムフックで自動的に機能するようにすることについてフィードバックがあります。

ソースコードを見ると、名前でカスタムフックをキャプチャするために正規表現を指定できるようにする意図があることがわかります。

https://github.com/facebook/react/blob/ba708fa79b3db6481b7886f9fdb6bb776d0c2fb9/packages/eslint-plugin-react-hooks/src/ExhaustiveDeps.js#L490 -L492

Reactチームは、依存関係配列を持つカスタムフックの追加の命名規則についてどう思いますか? フックは、このプラグインによってフックとして検出されるように、接頭辞useの規則に従っています。 特定の依存関係に依存するカスタムフックを検出するために提案する規則は、 WithDepsような後置のようなものです。つまり、完全なカスタムフック名はuseCustomHookWithDepsようなものになります。 WithDeps接尾辞は、最後の配列引数が依存関係の1つであることをプラグインに通知します。

プラグインは引き続き正規表現をサポートできますが、ライブラリの作成者が、ライブラリの利用者にプラグインをすべてのカスタム用に明示的に構成するのではなく、 WithDepsで後置されたカスタムフックをエクスポートできるようにすることで、大きなメリットが得られると思います。サードパーティまたはその他をフックします。

それは警告し、カスタムの同等性チェックを自動的に削除します。

const myEqualityCheck = a => a.includes('@');

useCallback(
  () => {
    // ...
  },
  [myEqualityCheck(a)]
);

useEffectの場合、「不必要な依存関係」の警告が表示されるべきではないと思います。これらの「依存関係」によって、効果の発火方法が変わるからです。

親と子の2つのカウンターがあるとしましょう。

  • カウンターは個別にインクリメント/デクリメントできます。
  • 親カウンターが変更されたときに子カウンターをゼロにリセットしたい。

実装:

  const [parent, setParent] = useState(0);
  const [child, setChild] = useState(0);

  useEffect(
    () => {
      setChild(0);
    },
    [parent] // "unnecessary dependency: 'parent'"
  );

徹底的なdepsルールは警告React Hook useEffect has an unnecessary dependency: 'parent'. Either exclude it or remove the dependency array.与えます

screen shot 2019-02-25 at 11 06 40 am

アプリの動作が変わるため、リンターがこの提案を行うべきではないと思います。

  1. CodeSandbox: https ://codesandbox.io/s/ol6r9plkv5
  2. 手順: parentが変更されると、 childはゼロにリセットされます。
  3. 意図:依存関係に不要のフラグを立てる代わりに、リンターはparentがuseEffectの動作を変更することを認識し、それを削除するようにアドバイスするべきではありません。

[編集]

回避策として、私は次のようなものを書くことができます

const [parent, setParent] = useState(0)
const [child, setChild] = useState(0)
const previousParent = useRef(parent)

useEffect(() => {
  if (parent !== previousParent.current) {
    setChild(0)
  }

  previousParent.current = parent
}, [parent])

しかし、私が好きな元のスニペットには「詩」があります。

問題は、依存関係の配列を、パフォーマンスの最適化と実際のコンポーネントの動作の単なるメカニズムとして解釈する必要があるかどうかにあると思います。 React Hooks FAQは、パフォーマンスを使用する理由の例としてパフォーマンスを使用していますが、パフォーマンスを向上させるために依存関係配列のみを使用する必要があることを意味するように例を解釈しませんでした。代わりに、エフェクト呼び出しをスキップする便利な方法と見なしました。一般に。

これは実際には、内部キャッシュを無効にするために数回使用したパターンです。

useEffect(() => {
  if (props.invalidateCache) {
    cache.clear()
  }
}, [props.invalidateCache])

このように使用するべきではない場合は、遠慮なくお知らせください。このコメントは無視してください。

@MrLeebo
どうですか

  const [parent, setParent] = useState(0);
  const [child, setChild] = useState(0);
  const updateChild = React.useCallback(() => setChild(0), [parent]);

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

私もあなたのその断片を理解しません。 依存関係はコードに基づいてのみ想定できますが、バグである可能性があります。 私の提案は必ずしもそれがそれを少なくともより明確にすることを解決するわけではありませんが。

これは以前にgetDerivedStateFromProps解決されたようです。 getDerivedStateFromPropsを実装するには

@nghiepit最初の投稿で必要なチェックリスト(CodeSandboxなど)を無視したため、コメントを

@ eps1lon useCallbackについてもまったく同じ警告が表示されます。一般的に、元のパターンよりも改善されているというパターンには同意しませんが、トピックを狂わせてそれについて話すことはしたくありません。 明確にするために、不要な依存関係ルールは、効果的なロジックを含めることができるため、特にuseEffectまたはuseLayoutEffectに対して緩和する必要があると思いますが、ルールはuseCallbackはそのままにしておく必要があります。 useMemoなど

@MrLeeboがここで説明したのと同じ問題/メンタルモデルの質問のいくつかにuseEffect依存関係のルールをそれほど厳密にすることはできないということです。 基本的な概念実証のアイデアのために取り組んでいた、信じられないほど不自然な例があります。 私はこのコードがひどくて、その考えが特に有用ではないことを知っています、しかしそれは目前の問題の良い例証であると思います。 @MrLeeboは、私が非常によく持っている質問を表現したと思います。

問題は、依存関係の配列を、パフォーマンスの最適化と実際のコンポーネントの動作の単なるメカニズムとして解釈する必要があるかどうかにあると思います。 React Hooks FAQは、パフォーマンスを使用する理由の例としてパフォーマンスを使用していますが、パフォーマンスを向上させるために依存関係配列のみを使用する必要があることを意味するように例を解釈しませんでした。代わりに、エフェクト呼び出しをスキップする便利な方法と見なしました。一般に。

https://codesandbox.io/s/5v9w81j244

私は、具体的に見てねuseEffect中にフックuseDelayedItems 。 現在、依存関係配列にitemsプロパティを含めると、リンティングエラーが発生しますが、そのプロパティまたは配列を完全に削除すると、望ましくない動作が発生します。

useDelayedItemsフックの基本的な考え方には、アイテムの配列と構成オブジェクト(ミリ秒単位の遅延、初期ページサイズ)が与えられます。最初は、構成されたページサイズに応じてアイテムのサブセットが渡されます。 config.delayが経過すると、アイテムはアイテムのフルセットになります。 新しいアイテムのセットが渡されると、フックはこの「遅延」ロジックを再実行する必要があります。 このアイデアは、大きなリストの遅延レンダリングの非常に粗雑で馬鹿げたバージョンです。

これらのルールに関するすべての作業に感謝します。開発する際に非常に役立ちました。 私の例の品質が疑わしい場合でも、これらの演算子がどのように使用されるかについての洞察が得られることを願っています。

編集:動作意図に関するコードサンドボックス内にいくつかの明確なコメントを追加しました。 情報が十分であることを願っていますが、それでも混乱することがあれば教えてください。

他の値を組み合わせた単一の値はどうですか?

例:fullNameはfirstNameとlastNameから派生します。 fullNameが変更されたとき(ユーザーが[保存]をクリックしたときなど)にのみエフェクトをトリガーしたいが、エフェクトで構成された値にもアクセスしたい

デモ

fullNameが変更された後にのみエフェクトを実行したいので、依存関係にfirstNameまたはlastNameを追加すると問題が発生します。

@aweary useEffectプロップ変更の間接わかりません。 あなたのonClickがその「効果」を処理しているようです。

https://codesandbox.io/s/0m4p3klpyw

他の値を組み合わせた単一の値に関しては、 useMemoがおそらくあなたが望むものになるでしょう。 あなたの例の計算の遅れた性質は、それがあなたのリンクされた振る舞いと正確に1:1でマッピングされないことを意味します。

これらの例のコードサンドボックスリンクを作成し、この投稿を編集します。

非常に単純なルールがあります。現在のタブが変更された場合は、一番上までスクロールします。
https://codesandbox.io/s/m4nzvryrxj

useEffect(() => {
    window.scrollTo(0, 0);
  }, [activeTab]);

そして私はReact Hook useEffect has an unnecessary dependency: 'activeTab'. Either exclude it or remove the dependency arrayを手に入れました

また、いくつかのコンポーネントを移行するときに、componentDidMountの動作を複製したいと思います。

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

このルールはこれについて激しく不平を言っています。 useEffect配列にすべての依存関係を追加することを躊躇しています。

最後に、useEffectの前に新しいインライン関数を宣言すると、次のようになります。
https://codesandbox.io/s/nr7wz8qp7l

const foo = () => {...};
useEffect(() => {
    foo();
  }, []);

あなたが得る: React Hook useEffect has a missing dependency: 'foo'. Either include it or remove the dependency array
依存関係リストにfooを追加することについてどう思うかわかりません。 各レンダリングでfooは新しい関数であり、useEffectは常に実行されますか?

@ ksaldana1副作用をイベントハンドラー内に配置するだけでは不十分です。 これにより、実際の更新がコミットされる前に副作用が発生し、必要以上に頻繁に副作用がトリガーされる可能性があります。

また、 useReducer使用している場合は、更新された状態がイベントハンドラー内で使用できないため、機能しません。

他の値を組み合わせた単一の値に関しては、 useMemoがおそらくあなたが望むものになるでしょう。

この例でuseMemo使用した場合、 firstNameまたはlastName変更されるたびにuseMemoが新しいfullNameを導出するため、破損します。 ここでの動作は、[保存]ボタンがクリックされるまでfullNameが更新されないことです。

@awearyこのようなものは機能しますか? https://codesandbox.io/s/lxjm02j50m
推奨される実装が何であるかを知りたいと思います。

また、あなたが述べたいくつかのことについてもっと知りたいです。 これらについての詳細を教えていただけますか?

ハンドラーでのトリガーと効果:

これにより、実際の更新がコミットされる前に副作用が発生し、必要以上に頻繁に副作用がトリガーされる可能性があります。

イベントハンドラーでuseReducer状態を使用する:

更新された状態は利用できません。

ありがとう!

@bugzpodder種類無関係なのが、スクロール・コールが内にある必要がありますuseLayoutEffectの代わりにuseEffect 。 現在、新しいルートに移動すると、目立ったちらつきがあります

これが意図的なものかどうかわからない:

小道具から関数を呼び出すと、リンターは小道具オブジェクト全体を依存関係として追加することを提案します。

短縮版:

useEffect(function() {
  props.setLoading(true)
}, [props.setLoading])

// ⚠️ React Hook useEffect has a missing dependency: 'props'.

フルバージョン: Codesandbox

もう一度やり直してください...しかし今回はもっと簡単です;)

小道具で、または実際にはどこからでも関数を渡し、フック内でその関数を使用すると、 exhaustive-deps警告が表示されます。

だから私は明らかに関数を依存関係配列に追加することができますが、それは関数であり決して変更されないので、これは不必要ですよね?

->サンドボックス

それがあなたが必要とするすべてであることを願っています、しかしそれが私が意味したことを示したので、私は単に@siddharthkpのサンドボックスをフォークしました。

THX。

だから私は明らかに関数を依存関係配列に追加することができますが、それは関数であり決して変更されないので、これは不必要ですよね?

これは正しくありません。 関数は、親が再レンダリングするときに常に変更される可能性があります。

@siddharthkp

小道具から関数を呼び出すと、リンターは小道具オブジェクト全体を依存関係として追加することを提案します。

これは、技術的にはprops.foo()がprops自体をthisからfoo呼び出しとして渡すためです。 したがって、 fooは暗黙的にprops依存する可能性があります。 ただし、この場合はもっと良いメッセージが必要です。 ベストプラクティスは常に破壊することです。

関数は、親が再レンダリングするときに常に変更される可能性があります。

もちろんですが、関数が親コンポーネントからではなく他の場所で定義されている場合、それは変更されません。

もちろんですが、関数が親コンポーネントからではなく他の場所で定義されている場合、それは変更されません。

直接インポートした場合、lintルールはエフェクトデップに追加するように要求しません。 レンダースコープ内にある場合のみ。 それがレンダースコープ内にある場合、lintルールはそれがどこから来ているのかわかりません。 今日は動的ではありませんが、誰かが親コンポーネントを変更すると、明日になる可能性があります。 したがって、それを指定することが正しいデフォルトです。 とにかく静的である場合、それを指定することは害はありません。

thx @gaearon

これは、技術的にはprops.foo()がprops自体をthisからfoo呼び出しとして渡すためです。 したがって、 fooは暗黙的にprops依存する可能性があります。 ただし、この場合はもっと良いメッセージが必要です。 ベストプラクティスは常に破壊することです。

それは私の質問にも答えます。 ありがとうございました! 😄

https://codesandbox.io/s/98z62jkyro

そのため、各入力のコンテキストで公開されているAPIを使用してすべての入力を登録し、入力の検証を処理するためのライブラリを作成しています。 useRegisterというカスタムフックを作成しました。

元:

 useEffect(
    () => {
      register(props.name, props.rules || []);
      console.log("register");
      return function cleanup() {
        console.log("cleanup");
        unregister(props.name);
      };
    },
    [props.name, props.rules, register, unregister]
  );

props.rulesを依存関係の一部にするように強制すると、無限のレンダリングループになってしまうようです。 Props.rulesは、バリデーターとして登録される関数の配列です。 配列を依存関係として提供する場合にのみ、この問題を検出しました。 私のコードサンドボックスでは、コンソールを開いてループしていることがわかります。

props.nameを依存関係として持つだけで、意図したとおりに機能します。 依存関係を強制すると、他の指摘どおりにアプリケーションの動作が変化し、この場合、副作用は深刻です。

@bugzpodder

Re: https :

useEffect(() => {
    window.scrollTo(0, 0);
  }, [activeTab]);

これは正当なケースのようです。 警告を緩和して、効果のみの無関係なデップを許可します。 (ただし、 useMemoまたはuseCallbackです。)

また、いくつかのコンポーネントを移行するときに、componentDidMountの動作を複製したいと思います。
このルールはこれについて激しく不平を言っています。 useEffect配列にすべての依存関係を追加することを躊躇しています。

申し訳ありませんが、その例を追加しなかったため、それについて話し合う機会を失いました。 そのため、OP投稿では具体的なUIの例をます。 あなたは最初のポイントのためにやったが、これはしなかった。 具体的な例を挙げていただければ幸いです。 詳細は本当にそれに依存します。

最後に、useEffectの前に新しいインライン関数を宣言すると、次のようになります: https ://codesandbox.io/s/nr7wz8qp7l

この場合の簡単な修正は、 doSomethingをエフェクトに移動することです。 その後、それを宣言する必要はありません。 または、 doSomethingあたりuseCallbackにすることもできます。 宣言されたdepsのみを使用する関数を省略できるように、ルールを緩和することはできます。 ただし、別の関数を呼び出す関数があり、それらの関数の1つに小道具または状態を追加すると、混乱する可能性があります。 突然、推移的に使用するすべてのエフェクト部門を更新する必要があります。 混乱する可能性があります。

これが新しいルールの機能リクエストなのか、それともexhaustive-depsについて改善できるものなのかわかりません…

import React from 'react'
import emitter from './thing'

export const Foo = () => {
  const onChange = () => {
    console.log('Thing changed')
  }

  React.useEffect(() => {
    emitter.on('change', onChange)
    return () => emitter.off('change', onChange)
  }, [onChange])

  return <div>Whatever</div>
}

onChange関数はレンダリングごとに作成されるため、 useEffectフック[onChange]引数は冗長であり、削除することもできます。

   React.useEffect(() => {
     emitter.on('change', onChange)
     return () => emitter.off('change', onChange)
-  }, [onChange])
+  })

リンターはそれを検出し、配列引数を削除するようにアドバイスすることができます。

配列アイテムのリストを維持している状況がありましたが、それらの1つ以上が作成されていて、とにかくレンダリングごとにフックが無効になっていることに気づきました。

[email protected]をリリースしたばかりで、このルールのいくつかの修正とより良いメッセージが含まれています。 画期的なことは何もありませんが、いくつかのケースを解決する必要があります。 来週は残りを見ていきます。

また、「安全な」関数depsを省略するための最初の可能なステップをhttps://github.com/facebook/react/pull/14996に投稿しました

@gaearon素晴らしいアイデア。 これは、フックを使用するときに、より良いスタイルを持つために間違いなく役立ちます🙏

@gaearonアクションが小道具から来た場合、それはまだ機能しません。 この例を考えてみましょう。

image

setScrollTopはreduxアクションです。

このSliderコンポーネントの例では、 Slider useEffectを使用して、DOMが使用可能になるまで待機し、noUiSliderコンポーネントをマウントできるようにしています。 したがって、 [sliderElement]を渡して、エフェクトの実行時にDOMで参照が使用できるようにします。 コンポーネントもサーバーでレンダリングするため、レンダリング前にDOMを使用できるようになります。 useEffect使用する他の小道具(つまり、最小、最大、onUpdateなど)は定数であるため、それらをエフェクトに渡す必要はありません。

screen shot 2019-03-02 at 5 17 09 pm


codesandboxに見られる効果は次のとおりです。

const { min, max, step } = props;
const sliderElement = useRef();

useEffect(() => {
  if (!sliderElement.current) {
    return;
  }

  const slider = sliderElement.current;
  noUiSlider.create(slider, {
    connect: true,
    start: [min, max],
    step,
    range: {
      min: [min],
      max: [max],
    },
  });

  if (props.onUpdate) {
    slider.noUiSlider.on('update', props.onUpdate);
  }

  if (props.onChange) {
    slider.noUiSlider.on('change', props.onChange);
  }
}, [sliderElement]);

@ WebDeg-ブライアン完全なCodeSandboxデモなしではお手伝いできません。 ごめん。 上の投稿を参照してください。

一般的な「機能は決して変わらない」という誤解について少し投稿しました。

https://overreacted.io/how-are-function-components-different-from-classes/

まったく同じトピックではありませんが、このルールに関連しています。

こんにちは@gaearon 、これはあなたが私にここに投稿するように頼んだ例です(ツイーターから):)

基本的に、ライブラリのreact-trapをフックに変換しようとしています。
これは、要素の外側/内側のイベントのトラップにすぎません。

私の問題は、 useEffectが状態値( trapped )に依存していない場合、古くなっていることがあるということです。
実演するためにいくつかのコメントとログを書きました。 useTrap.jsファイルを見てください。コメントとログは、 useEffect preventDefaultHelper関数と

私の知る限り、値がuseEffect内にない場合、その値はその依存関係の一部であってはなりません(間違っている場合は訂正してください)。

  1. CodeSandbox
  2. 手順:
    ユーザーがボックスの内側をクリックしてアクティブにし、外側をクリックして非アクティブにすることもできますが、最初のクリックではコンテキストメニューがトリガーされないはずです( e.preventDefault )。
    私が「ファーストクリック」と言うとき、私は状態を変える最初のクリックを意味します。
    アクティブなボックスを指定して、その外側を右クリックすると、状態が「非アクティブ」に変更され、コンテキストメニューが表示されなくなります。 外側をもう一度クリックしても状態には影響しないため、コンテキストメニューが表示されます。

ここで明確になり、ユースケースが理解できることを願っています。さらに情報を提供する必要がある場合はお知らせください。 ありがとう!

こんにちは@ gaearon 、Twitterで提案されたように、ここにカスタムフックを追加します! 依存関係をスキップしない正しい形を見つけるのに苦労しています。

例としては手の込んだものですが、わかりやすく説明できればと思います。

これが現在の状態です: react-async-utils / src / hooks / useAsyncData.ts

機能の概要
ユーザーが非同期呼び出しを処理し、結果のデータとプロセスに沿った状態を処理するのを支援します。

triggerAsyncDataは、 Promiseを返すgetData関数に従って、 asyncData状態を非同期的に更新します。 triggerAsyncDataは、エフェクトとして、またはフックユーザーが「手動で」呼び出すことができます。

課題

  1. triggerAsyncDataを呼び出すエフェクトの依存関係は、本質的なものです。 triggerAsyncDataはエフェクトの依存関係ですが、すべてのレンダリングで作成されます。 これまでの考え方:

    1. 依存関係として追加するだけです=>しかし、その後、エフェクトはすべてのレンダリングで実行されます。

    2. 依存関係として追加しますが、 useMemo / useCallbackとtriggerAsyncData => useMemo / useCallbackは、パフォーマンスの最適化にのみ使用する必要があります。私の知る限り。

    3. エフェクト内でスコープを設定する=>その後、ユーザーに返すことはできません。

    4. triggerAsyncDataを依存関係として使用する代わりに、 triggerAsyncData依存関係を依存関係として使用します=>これまでに見つけた最良のオプション。 しかし、それは「exhaustive-deps」ルールに違反します。

  2. カスタムフックのすべての入力パラメーターは、内部効果の依存関係になります。 そのため、パラメータとしてのインライン関数とリテラルオブジェクトにより、エフェクトが頻繁に実行されます。

    1. ユーザーの責任はユーザーに任せてください。 必要に応じてuseMemo / useCallbackを使用して、適切な値を提供します=>非常に頻繁に提供されないのではないかと思います。 そして、彼らがそうするならば、それはかなり冗長です。

    2. カスタムフックに追加の引数を許可して入力の詳細を提供し、入力自体の代わりにそれを使用します=>クールで、冗長性が低く、ユーザーの制御が強化されます。 私はこれを使用しています。 しかし、それは「exhaustive-deps」ルールに違反します。 (実際に使用しているのはこれであり、追加の引数が提供されていない場合は通常の部門にフォールバックします。これは強力なパターンだと思います)。

  3. カスタムフックの不適切に管理された依存関係は、内部効果のために無限の非同期ループを生成します。 私はこれを防ぐために防御プログラミングを使用しましたが、それは「偽物」を追加しますか? 依存関係( asyncData )。 これにより、「exhaustive-deps」ルールが再び破られます。

思ったより長い説明…でも、フックをきちんと使うのに苦労したことを反映していると思います。 これらの問題を明確にするために他にできることがあれば教えてください。

ここで頑張ってくれてありがとう!

ねえ@gaearonあなたのすべてのハードワークに感謝します。

  1. 最小限の非同期データフェッチの例CodeSandboxの例。

  2. ユーザーは5つのLoremのイプサムのタイトル文字列からフェッチ見ることが予想されるJSON API 。

  3. 目的のAPIを使用してデータをフェッチするためのカスタムフックを作成しました。

const { data, isLoading, isError } = useDataApi('https://jsonplaceholder.typicode.com/posts')

カスタムuseDataApiフックの内部:

...
  const fetchData = async () => {
    let response;
    setIsError(false);
    setIsLoading(true);

    try {
      response = await fetch(url).then(response => response.json());

      setData(response);
    } catch (error) {
      setIsError(true);
    }

    setIsLoading(false);
  };

  useEffect(
    () => {
      fetchData();
    },
    [url]
  );
...

このコードの問題

  useEffect(
    () => {
      fetchData();
    },
    [url]
  );

ここで、 react-hooks/exhaustive-depsは、依存関係配列にfetchDataを追加し、 urlを削除する必要があるという警告を発します。

このフックをに変更すると

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

その後、リクエストを継続的に発生させ、停止することはありません。これはもちろん大きな問題です。 コードにバグがあるのか​​、 react-hooks/exhaustive-depsが誤検知を引き起こしているのかわかりません。

助けていただければ幸いです。 本当にありがとう。

PS useEffectがデータフェッチにましたが、 react docsは、データuseEffectがデータフェッチに最適であるという確信を与えてくれたData fetching, setting up a subscription, and manually changing the DOM in React components are all examples of side effectsを主張しています。 だから今私は少し混乱しています😕

@ jan-stehlikはfetchDataをuseCallbackでラップする必要があります。 ここで必要な変更を加えてcodesandboxをフォークしましたhttps://codesandbox.io/s/pjmjxprp0m

とても助かりました、

私の知る限り、値がuseEffect内にない場合、その値はその依存関係の一部であってはなりません(間違っている場合は修正してください)。

私はあなたがここで間違っていると思います。 というか、少し混乱しています。 あなたは、使用しているhandleEventあなたの効果の内側。 しかし、あなたはそれを宣言しません。 そのため、読み取った値は古くなっています。

面白い。

エフェクト内でhandleEventを使用しています。 しかし、あなたはそれを宣言しません。 そのため、読み取った値は古くなっています。

どういう意味ですか:_ "しかし、あなたはそれを宣言しません" _?
効果の下で宣言されます(他のすべてのハンドラーと同じです)。

または、ハンドラーが状態値を使用していて、エフェクトがハンドラーをアタッチしているため、エフェクトがその状態値に依存していることを意味しますか?

または、ハンドラーが状態値を使用していて、エフェクトがハンドラーをアタッチしているため、エフェクトがその状態値に依存していることを意味しますか?

はい、あなたが機能を使用する場合、あなたはどちらかDEPSでそれを宣言する必要があります(その場合でそれをラップuseCallbackそれを再作成を避けるために)、またはすべてその関数が使用します。

わかりました、これは私にとってのニュースです! この入力をありがとう@gaearon :)
私(そして他の人?)にとってそれが非常に明確であることを望んでいます...

エフェクトが関数を呼び出したり、渡したり、何かを実行したりする場合は、次のいずれかのdeps配列に渡す必要があります。
関数自体ORこの関数が使用する変数。
関数が関数コンポーネント/カスタムフック内で宣言されている場合は、コンポーネントまたはカスタムフックが実行されるたびに再作成されないように、関数をuseCallbackラップすることをお勧めします。

私はそれをドキュメントで見なかったと言わなければなりません。
_Note_セクションに追加しても大丈夫だと思いますか?

ノート
入力の配列は、引数としてエフェクト関数に渡されません。 ただし、概念的にはそれが表されます。エフェクト関数内で参照されるすべての値は、入力配列にも表示される必要があります。 将来的には、十分に高度なコンパイラがこの配列を自動的に作成する可能性があります。

編集
もう1つ、私の例では、 handleEvent (またはその理由で他のハンドラー)をラップするときのuseCallbackのdepsは何ですか。 それはeventそれ自体ですか?

私はそれをドキュメントで見なかったと言わなければなりません。

ドキュメントには、「エフェクト関数内で参照されるすべての値は、入力配列にも表示される必要があります」と記載されています。 関数も値です。 これをより適切に文書化する必要があることに同意します—これがこのスレッドのすべてです:-)これ専用の新しい文書化ページのユースケースを収集しています。

もう1つ、私の例では、handleEvent(またはその理由で他のハンドラー)をラップするときのuseCallbackのdepsは何ですか。 それはそれ自体がイベントですか?

よく分からない。 これは、その外部の関数によって参照される任意の値です。 useEffect 。

関数を依存関係として、これを考え抜かなかったと思います。 私のメンタルモデルは間違っていました。コンポーネント/フックへの小道具または引数として提供された場合にのみ、関数を依存関係として渡すように考えていました。 これを明確にしてくれてありがとう。

useCallback 、私は次のように使用しました。

const memoHandleEvent = useCallback(
    handleEvent
);

そしてもちろん渡さmemoHandleEventするために、依存関係としてuseEffectウェルにとしてaddEventListener INSEAD本物のhandleEvent機能。 うまくいくようですが、これが適切で慣用的な方法であることを願っています。

2番目の引数がないuseCallbackは何もしないことに注意してください。

繰り返しますが、完全なサンドボックスが必要になります。 このような不完全な説明では、修正が正しいかどうかわかりません。

2番目の引数のないuseCallbackは何もしないことに注意してください。

アーグ! :しかめっ面:笑

繰り返しますが、完全なサンドボックスが必要になります。 このような不完全な説明では、修正が正しいかどうかわかりません。

ああ、これは上からの同じリンクです。 私はちょうどそれを更新しました:)

CodeSandboxリンクを更新しないでください:PIは元々のようにそれらを必要とします—そうでなければ、それらのlintルールをテストできません。 2つの異なるソリューションがある場合、2つの別々のサンドボックスを作成していただけますか? だから私はそれぞれをチェックすることができます。

おっとごめん! :PIが私のアカウントのサンドボックスの数を超えました。 いくつか削除させてください。別のものを作成します(そして元の変更を元に戻します)。

@gaearonこれはuseCallbackを使用したソリューションへの2番目のリンクです

大丈夫だと思うシナリオがありますが、リンターは文句を言います。 私のサンプル:

CodeSandbox

サンプルが表現しようとしているのは、ユーザーがボタンをクリックすると、以前に提供されたIDと関数に基づいて新しいデータを要求するように要求されます。 IDが変更された場合、新しいデータを要求するべきではありません。 リロードの新しいリクエストのみが新しいデータリクエストをトリガーする必要があります。

ここでの例は少し不自然です。 私の実際のアプリケーションでは、Reactははるかに大きなWebアプリケーションのごく一部であるDIVに存在します。 渡される関数は、ReduxとmapDispatchToPropsを介して行われます。ここで、アクションの作成はIDを取得し、データをフェッチしてストアを更新するためのajaxリクエストを行います。 refreshRequestプロップはReact.createElementを介して渡されます。 私の元の実装では、クラスコンポーネントに次のようなコードがありました。

componentDidUpdate (prevProps) { const { getData, someId, refreshRequest} = this.props; if (prevProps.refreshRequest!== this.props.refreshRequest) { getData(someId); } }

エフェクトフックを使用して同じ動作を実装しようとしています。 しかし、サンプルに書かれているように、リンターは次のように不平を言います。

警告ReactHook useEffectには、依存関係がありません: 'getData'および 'someId'。 それらを含めるか、依存関係配列を削除してください

リンターが必要とするすべてのものを追加すると、ユーザーがサンプルのいずれかのボタンをクリックすると、useEffectがトリガーされます。 ただし、[新しいデータの要求]ボタンが押されたときにのみトリガーされるようにします。

うまくいけば、それは理にかなっています。 不明な点がある場合は、さらに明確にさせていただきます。 ありがとうございました!

ベア関数の依存関係を検出するための実験的なサポートがある[email protected]を公​​開しました( useCallbackなしではあまり役に立たない傾向があります)。 これがgifです:

demo

あなたのプロジェクトでそれを試してみて、それがどのように感じられるかを見てみてください! (https://github.com/facebook/react/pull/15026でその特定のフローにコメントしてください。)

明日、このスレッドの例で試してみます。

まだ試していませんが、巻き上げに対応しているのではないでしょうか。 これは私がその場で思いついた「最小限の例」にすぎないため、何の役にも立ちませんが、 returnステートメントを見やすくするために、ホイスト宣言を頻繁に使用しています。

function Component() {
  useEffect(() => {
    handleChange
  }, [handleChange])

  return null

  function handleChange() {}
}

リンターに関する限り、ルールにuseEffectように動作するように別の関数を構成するオプションがあると便利です。 たとえば、これを追加して、AJAX呼び出しを行うエフェクトに非同期関数を簡単に使用できるようにしました。ただし、このようにすると、 exhaustive-depsリンティングの利点がすべて失われます。

const useAsyncEffect = (fn, ...args) => {
    /* eslint-disable react-hooks/exhaustive-deps */
    useEffect(() => {
        fn();
    }, ...args);
};

編集:気にしないでください、これはルールのadditionalHooksオプションを使用してすでに可能であることに気づきました。

こんにちは、みんな、
https://codesandbox.io/s/znnmwxol7lの例があり

以下は私が欲しいものです:

function App() {
  const [currentTime, setCurrentTime] = React.useState(moment());

  const currentMonth = React.useMemo(
    () => {
      console.log("RUN");
      return currentTime.format("MMMM");
    },
    [currentTime.format("MMMM")] // <= this proplem [currentTime]
  );

  return (
    <div className="App">
      <h1>Current month: {currentMonth}</h1>
      <div>
        <button
          onClick={() => setCurrentTime(currentTime.clone().add(1, "days"))}
        >
          + 1 day
        </button>
      </div>
      <div>{currentTime.toString()}</div>
    </div>
  );
}

しかし、これは私が得るものです:

  const currentMonth = React.useMemo(
    () => {
      console.log("RUN");
      return currentTime.format("MMMM");
    },
    [currentTime] // <= this proplem
  );

currentTime変更されると、 currentMonth不要に再計算されます

またはどういうわけか私はそれを以下で行うことができます:

  const currentMonth = React.useMemo(
    () => {
      return currentTime.format("MMMM");
    },
    [myEqualityCheck(currentTime)]
  );

これがすでに回答されている場合は申し訳ありませんが、上のスレッドでそれを見ることができませんでした:
マウントでのみuseEffectフックを実行する方法は、2番目のパラメーターとして空の入力配列を定義することです。 ただし、これを行うと、これらの入力引数を含めることについての徹底的な苦情があり、更新時にも実行されるように効果が変更されます。
完全なdepsが有効になっているマウントでのみuseEffectを実行するためのアプローチは何ですか?

@einarqマウント上のuseEffectれているすべての値が決して変更されないようにする必要があると思います。 これは、 useMemoような他のフックを使用して実現できます。 その後、このESlintルールがすべての参照を(自動修正を使用して)配列に追加するかどうかに関係なく、コードは1回だけ実行されます。

@einarqスレッドの他の場所で述べられているように、CodeSandboxなしではお手伝いできません。 答えは本当にあなたのコードに依存するからです。

@nghiepitあなたの例は私には本当に意味がありません。 入力がcurrentTime.format("MMMM")場合、すでに計算されているため、 useMemoは何も最適化しません。 つまり、不必要に2回計算しているだけです。

additionalHooksオプションのコールバックである引数インデックスを指定することは可能ですか? コードでは、これが最初のhttps://github.com/facebook/react/blob/9b7e1d1389e080d19e71680bbbe979ec58fa7389/packages/eslint-plugin-react-hooks/src/ExhaustiveDeps.js#L1051-であると想定していることがわかり

現在はできません。 私たちの一般的なガイダンスは、カスタムフックはdeps引数を持たないことを好むべきであるということです。なぜなら、depsがどのように構成されているかを考えるのが非常に難しくなるからです。 関数を使用する場合は、代わりにユーザーにuseCallback要求することができます。

@CarlosGines OPの投稿で要求されているように、CodeSandboxを作成していただけませんか。 私はこれを理由で尋ねています—そうでなければ、変更を確認するのは難しいです。 特にTypeScriptで書かれている場合。

[email protected]を公​​開しましたが、より便利なメッセージとよりスマートなヒューリスティックが含まれていることを願っています。 安定する前に、プロジェクトを試してみてください。

このスレッドのほとんどの例で、問題の修正に役立つ合理的なアドバイスが得られることを期待しています。

それは[email protected]ますか?

はい。

実際には、いくつかの小さな変更を加えて[email protected]を公​​開しました。

@gaearon万が一、コードサンドボックス内でeslintを機能させることができましたか?

@einarq多分このようなものはこれでうまくいくでしょうか?

const useDidMount = fn => {
  const callbackFn = useCallback(fn)
  useEffect(() => {
    callbackFn()
  }, [callbackFn])
}

(私のスニペットに基づいて)単純なCRAアプリでプラグインを動作させることができないと言うのは実際には恥ずかしいことです。
これがcodesandboxからのフォークされたリポジトリです。

私はドキュメントでこのメモを見ました:

注:Create React Appを使用している場合は、直接追加するのではなく、このルールを含むreact-scriptsの対応するリリースをお待ちください。

しかし、現時点ではCRAでテストする方法はありませんか? 👂

ええ、ESLint構成に追加するまで、それを取り出してESLint構成に追加する必要があります。 ブランチでイジェクトすることはできますが、マージすることはできません。 :-)

やあ、

まず第一に:コミュニティと緊密に協力してくれてありがとう、そしてあなたが一般的にやっている素晴らしい仕事に感謝します!

Fetch APIを中心に構築したカスタムフックに問題があります。 この問題を示すためにCodesandboxを作成しました。

Codesandboxの例

https://codesandbox.io/s/kn0km7mzv

注:この問題(以下を参照)は無限ループになり、問題のデモンストレーションに使用しているDDoS jsonplaceholder.typicode.comはしたくありません。 そのため、カウンターを使用した単純なリクエストリミッターを含めました。 これは問題を実証するために必須ではありませんが、この素晴らしいプロジェクトに無制限の量のリクエストを送信するのは間違っていると感じました。

説明

アイデアは、APIリクエストの3つの可能な状態(読み込み、成功、エラー)の処理を簡単にすることです。 したがって、3つのプロパティisLoading 、 response 、およびerror. It makes sure that either応答orエラーis set and updatesを返すカスタムフックuseFetch()を作成しました。 is set and updates isLoading . As the name implies, it uses the Fetch`API。

そのために、3つのuseStateフックを使用します。

  const [isLoading, setIsLoading] = useState(false);
  const [response, setResponse] = useState(null);
  const [error, setError] = useState(null);

およびuseEffectフック:

useEffect(
    () => {
      fetch(url, fetchConfig)
        .then(/* Handle fetch response... See Codesandbox for the actual implementation */)
    },
    [url]
  );

それは長いの依存関係の配列としてとして罰金を働くuseEffect唯一含まれている[url] 。 fetchConfig (= [url, fetchConfig] )を追加すると、無限ループになります。 特定のユースケースでは、 urlが変更された場合にのみエフェクトを再実行するだけで十分ですが、リンターでは[url]のみを使用できません( v1.4.0テスト済みv1.5.0-beta.1 )。

カスタムフックの最後に、3つの状態変数がオブジェクトとして返されます。

  return {
    error,
    isLoading,
    response
  };

リンティングの提案が理にかなっているので、これがこの問題に関するガイダンスを求めるのに適切な場所であるかどうかはわかりません。 そうでない場合は申し訳ありません。

fetchConfigが変更された場合

わかった。 先週の修正と改善を加えて[email protected]をリリースしました。
このスレッドは、問題の原因となるさまざまなパターンを見つけるのに非常に役立ちました。

本当にありがとうございました。 (特にサンドボックスを提供した人。:-)


ケースが多いので、個人的には詳しくお答えしません。

代わりに、今後数日で一般的なレシピと落とし穴を作成し、ドキュメントからそれらにリンクします。 このスレッドのすべての一般的なパターンがカバーされていることを確認します(または、まれなパターンについては別の問題でフォローアップするように依頼します)。

例が公開されたら、ここにコメントし、CodeSandboxを含むすべてのコメントを編集して、適切な修正を示す関連する回答/例へのリンクを追加します。 これがあなたと将来の読者の助けになることを願っています。

乾杯!

❤️

@timkrautは、depsにfetchConfigを追加できるはずです。また、コンポーネントは、参照が残るようにメモでラップする必要があります。
例: https :


これに関する私の問題は、コンポーネントがフックの実装の詳細を認識する必要があることです...

このスレッドがまだ例の議論に開かれているかどうかはわかりませんが、ベストプラクティスに頭を悩ませています。 私の質問は、useEffectフックが依存関係配列で起動するタイミングを制御し、その時点での値を使用するのではなく、lintルールを回避するために別のrefを宣言する必要があることです。

例

https://codesandbox.io/s/40v54jnkyw

この例では、入力値を定期的に自動保存しようとしています。

説明

5秒ごとに、コードは現在の値を自動保存しようとしています(今のところ画面に出力するだけです)。 リンターでは、すべての入力値を依存関係配列に含める必要があります。これにより、エフェクトが発生する頻度が変わります。

  useEffect(
    () => {
      if (autoSaveTick % 5 === 0) {
        setAutosaveValue(
          `${input1Value.value}, ${input2Value.value}, ${input3Value.value}`
        );
      }
    },
    [autoSaveTick]
  );

私が見る代替案は、例で定義されたuseTick useIntervalフックと

前の変数変更(変数A)時に他の変数値(変数Bと変数C)を使用する1つの変数が変更されたときに実行される効果(変数A)を作成するためのベストプラクティスを探しています。

fetchConfigが変更された場合

依存関係の配列に追加しても問題ありません。 私たちの特定のビジネスケースでは、これは決して起こり得ませんが、とにかくそれを追加することは良い考えだと思います。

これに関する私の問題は、コンポーネントがフックの実装の詳細を認識する必要があることです...

これもまさに私たちが抱えている問題です:/この実装では、本体の設定を簡単にするために追加のプロパティを使用しているため、このフックを使用するたびに2つのuseMemo()呼び出しを使用する必要があります。 この制限を克服する方法はまだわかりません。 あなたがアイデアを持っているなら、私に知らせてください!

空の配列を渡すときにルールが文句を言わずに、依存関係が1回だけあるuseEffectを実行するにはどうすればよいですか?

たとえば、次のようになります。

useEffect(() => { init({ dsn, environment}) }, [])

これに関する私の問題は、コンポーネントがフックの実装の詳細を認識する必要があることです...

これらが実装の詳細であるとは言えません。 それはあなたのAPIです。 オブジェクトが渡された場合、いつでも変更できると想定しています。

実際には常に静的である場合は、フックファクトリへの引数にすることができます。 createFetch(config)を返すuseFetch() 。 あなたはトップレベルで工場を呼びます。

少し変だと思います。 現在取り組んでいるuseSubscriptionも同様の問題があります。 しかし、これは一般的な問題であるため、これはuseMemo実際には正当な答えであり、人々はこれらの場合にそれを行うことに慣れるべきであることを意味するかもしれません。

実際には、これについては将来さらに検討する予定です。 ただし、動的な可能性のあるオブジェクトを静的なオブジェクトとして扱うべきではありません。そうすると、ユーザーはオブジェクトを変更できなくなります。

@asylejmani上部の投稿で要求されているように、ユースケースに関する詳細は提供されていません。 なぜ誰かがあなたの質問に答えることができると期待するのですか?

ルールのポイントは、 environmentとdsnは時間の経過とともに変化する可能性があり、コンポーネントがそれを処理する必要があることを通知することです。 したがって、コンポーネントにバグがある(これらの値の変更を処理しないため)か、問題にならない独自のケースがあります(この場合、lintルールを追加して理由を説明するコメントを無視する必要があります)。

どちらの場合も、あなたが何を求めているのかは明確ではありません。 ルールは物事が変わる可能性があると不平を言います、そしてあなたはその変化を処理しません。 完全な例がなければ、あなただけがそれを扱う必要がないと思う理由に答えることができます。

@gaearon明確になっていないことをお詫びします(頭の中で常に明確に聞こえます):)私の質問は、useEffectを使用してcomponentDidMountをどのように実現するかというようなものです。

空の配列よりも印象に残っていましたが、ルールでは常に依存関係を配列に含めるように指示されています。

@asylejmani componentDidMountようなクラスライフサイクルメソッドの最大の落とし穴は、それを分離されたメソッドと考える傾向があることだと思いますが、実際にはフローの一部です。
componentDidMount何かを参照する場合は、おそらくcomponentDidUpdateでも処理する必要があります。そうしないと、コンポーネントにバグが発生する可能性があります。
これはルールが修正しようとしていることであり、時間の経過とともに値を処理する必要があります。

  1. コンポーネントがマウントされ、小道具で何かをする
  2. コンポーネントが更新されました(例:小道具の値が変更されました)、新しい小道具の値で何かをします

番号1は、ロジックをcomponentDidMount / useEffect本体に配置する場所です。
2番目は、ロジックをcomponentDidUpdate / useEffect depsに配置する場所です。

ルールはあなたがフローの2番目の部分をやっていないと不平を言っています

@gaearon明確になっていないことをお詫びします(頭の中で常に明確に聞こえます):)私の質問は、useEffectを使用してcomponentDidMountをどのように実現するかというようなものです。

空の配列よりも印象に残っていましたが、ルールでは常に依存関係を配列に含めるように指示されています。

私と@asylejmaniはここで同じページにいると思いますが、 @ gaearonが言っていることは、実際に依存関係がある場合にのみマウントでエフェクトを実行するのはおそらく間違っているということだと思います。
それは公正な声明ですか? 空の配列を提供することは、「自分が何をしているのかわかっている」と言っているようなものだと思いますが、ルールをそのままにしておきたい理由はわかります。

サンドボックスをまだ提供していないことをお詫びします。 先日、Create React Appの例から始めましたが、サンドボックスでeslintを実行する方法がわかりませんでした。その後、最初に保存せずにブラウザーをリロードするとサンドボックスが失われました(CodeSandboxの一時保存が保存されていると仮定しました。
それから私は寝なければならなかった、そしてそれ以来時間がなかった。

いずれにせよ、私はあなたが言っていることを理解します。通常のシナリオでは、これらの依存関係を含めるのがおそらく最善であり、マウントのみで実行するのに十分であるとは想定していません。

そのための有効なユースケースもおそらくありますが、説明したり、良い例を考え出すのは少し難しいので、必要に応じてインラインでルールを無効にしていきます。

@asylejmaniは、 https: //github.com/facebook/react/issues/14920#issuecomment -466378650に似たユースケースですか? この場合、ルールがシナリオを理解することは不可能だと思うので、そのタイプのコードでは手動で無効にする必要があります。 他のすべての場合、ルールは正常に機能します。

これが理にかなっているかどうかはわかりませんが、私にとって非常に一般的なシナリオの1つは、次のようなものです。

useEffect(() => {
    if(!dataIsLoaded) { // flag from redux
        loadMyData(); // redux action creator
    }
}, []);

これらの依存関係は両方ともreduxから来ています。 データ(この場合)は1回だけロードする必要があり、アクションの作成者は常に同じです。

これはreduxに固有であり、あなたのeslintルールはこれを知ることができないので、なぜ警告する必要があるのか​​わかります。 空の配列を提供するだけでルールを無効にすべきかどうかまだ疑問に思っていますか? すべてではなく一部を提供した場合、またはまったく提供しなかった場合に、ルールがdepsの欠落について教えてくれるのが好きです。 空の配列は私とは違う意味を持っています。 しかし、それは私だけかもしれません:)

頑張ってくれてありがとう! そして、開発者としての私たちの生活をより良くするために:)

私のユースケースははるかに単純で、もちろんすべての依存関係を追加でき、それでも同じように機能しますが、いくつかの依存関係があるが他の依存関係がない場合、ルールは「警告」するという印象を受けました。

@einarqのユースケースは、私が数回使用したものです。たとえば、「componentDidMount」では、データがない場合(reduxなどから)にデータをロードします。

また、これらの場合、インラインでルールを無効にすることが最善の選択であることに同意します。 その場合、あなたは自分が何をしているのかを正確に知っています。

私の全体的な混乱は[]対[いくつか]だったと思います、そしてもちろん素晴らしい仕事をしてくれたます:)

私と@asylejmaniはここで同じページにいると思いますが、 @ gaearonが言っていることは、実際に依存関係がある場合にのみマウントでエフェクトを実行するのはおそらく間違っているということだと思います。 それは公正な声明ですか?

はい。 コンポーネントが小道具の更新を処理しない場合、通常はバグがあります。 useEffectのデザインは、それに立ち向かうことを余儀なくさせます。 もちろん回避することもできますが、デフォルトでは、これらのケースを処理するように微調整します。 このコメントはそれをよく説明しています: https ://github.com/facebook/react/issues/14920#issuecomment-470913287。

これらの依存関係は両方ともreduxから来ています。 データ(この場合)は1回だけロードする必要があり、アクションの作成者は常に同じです。

それが同じであれば、依存関係にそれを含めても害はありません。 強調したいのですが、依存関係が決して変わらないことが確実であれば、それらをリストしても害はありません。 ただし、後で変更が発生した場合(たとえば、親コンポーネントが状態に応じて異なる機能を渡す場合)、コンポーネントはそれを正しく処理します。

空の配列を提供するだけでルールを無効にすべきかどうかまだ疑問に思っていますか?

いいえ。空の配列を提供してから、一部の小道具や状態が古くなっている理由を疑問に思うことは、文字通り最も一般的な間違いです。

>>

たくさんの意味があります、ありがとう

2019ĺš´3月8日には、午前15時27分で、ダン・アブラモフ監督の[email protected]は書きました:

私と@asylejmaniはここで同じページにいると思いますが、 @ gaearonが言っていることは、実際に依存関係がある場合にのみマウントでエフェクトを実行するのはおそらく間違っているということだと思います。 それは公正な声明ですか?

はい。 コンポーネントが小道具の更新を処理しない場合、通常はバグがあります。 useEffectの設計は、それに立ち向かうことを強制します。 もちろん回避することもできますが、デフォルトでは、これらのケースを処理するように微調整します。 このコメントはそれをよく説明しています:#14920(コメント)。

これらの依存関係は両方ともreduxから来ています。 データ(この場合)は1回だけロードする必要があり、アクションの作成者は常に同じです。

それが同じであれば、依存関係にそれを含めても害はありません。 強調したいのですが、依存関係が決して変わらないことが確実であれば、それらをリストしても害はありません。 ただし、後で変更が発生した場合(たとえば、親コンポーネントが状態に応じて異なる機能を渡す場合)、コンポーネントはそれを正しく処理します。

空の配列を提供するだけでルールを無効にすべきかどうかまだ疑問に思っていますか?

いいえ。空の配列を提供してから、一部の小道具や状態が古くなっている理由を疑問に思うことは、文字通り最も一般的な間違いです。

—
あなたが言及されたのであなたはこれを受け取っています。
このメールに直接返信するか、GitHubで表示するか、スレッドをミュートしてください。

@aweary

これにより、実際の更新がコミットされる前に副作用が発生し、必要以上に頻繁に副作用がトリガーされる可能性があります。

これが何を意味するのかわかりません。 例を挙げていただけますか?

今日は@threepointoneでこれらをパスしました。 要約は次のとおりです。

Lintルールで修正されました

無関係なuseEffect依存関係

正当なシナリオがあるため、このルールは、 useEffect 「外部」のdepを追加することを妨げるものではありません。

同じコンポーネント内で機能しますが、エフェクトの外部で定義されます

Linterは、現在安全である場合には警告しませんが、それ以外の場合はすべて、より良い提案を提供します(関数をエフェクト内に移動したり、 useCallbackラップしたりするなど)。

ユーザーコードで修正する価値がある

小道具の状態をリセットすると変化します

これでlint違反は発生しなくなりましたが、小道具に応じて状態をリセットする慣用的な方法は異なります。 このソリューションでは、レンダリングに一貫性がないため、望ましいかどうかはわかりません。

「私の非関数値は一定です」

フックは、可能な限り正確に向かってあなたを少しずつ動かします。 あなたは(いくつかのケースでは、あなたが省略できます)DEPSを指定した場合、私たちは強く、あなたが変更されないと思うことも、ものを含めることをお勧めします。 はい、このuseDebounce例では、遅延が変わる可能性は

特定の値が静的であると絶対に主張する場合は、それを強制することができます。
最も安全な方法は、APIで明示的に行うことです。

const useFetch = createFetch({ /* config object */});
const useDebounce = createDebounce(500);
const FormInput = createInput({ rules: [emailValidator, phoneValidator] });

次に、レンダリング内に配置しない限り、明らかに変更することはできません。 (これはフックの慣用的な使用法ではありません。)しかし、 <Slider min={50} />は決して変更できないと言うことは、実際には有効ではありません。誰かが簡単に<Slider min={state ? 50 : 100} />に変更する可能性があります。 実際、誰かがこれを行うことができます:

let slider
if (isCelsius) {
  slider = <Slider min={0} max={100} />
} else {
  slider = <Slider min={32} max={212} />
}

誰かがisCelsius状態を切り替えた場合、 minが変更されないと想定しているコンポーネントは更新に失敗します。 この場合、 Sliderが同じものになるかどうかは明らかではありません(ただし、ツリー内で同じ位置にあるためです)。 したがって、これはコードに変更を加えるという点で主要な足がかりです。 Reactの主なポイントは、更新が初期状態と同じようにレンダリングされることです(通常、どちらがどちらであるかはわかりません)。 小道具の値Bをレンダリングするか、小道具の値AからBに移動するかにかかわらず、外観と動作は同じである必要があります。

これはお勧めできませんが、場合によっては、強制メカニズムは、値が変更されたときに警告するフックである可能性があります(ただし、最初のメカニズムを提供します)。 少なくともその場合は、気付かれる可能性が高くなります。

function useMyHook(a) {
  const initialA = usePossiblyStaleValue(a);
  // ...
}

function usePossiblyStaleValue(value) {
  const ref = useRef(value);

  if (process.env.NODE_ENV !== 'production') {
    if (ref.current !== value) { // Or your custom comparison logic
      console.error(
        'Unlike normally in React, it is not supported ' +
        'to pass dynamic values to useMyHook(). Sorry!'
      );
    }
  }

  return ref.current;
}

更新を処理できないという正当なケースもあるかもしれkeyをリセットするラッパーコンポーネントを作成して、新しい小道具でクリーンな再マウントを強制することもできます。 これは、スライダーやチェックボックスなどのリーフコンポーネントにおそらく適しています。

「私の関数値は一定です」

まず第一に、それが一定でトップレベルのスコープに引き上げられている場合、リンターは文句を言いません。 しかし、それは小道具や文脈から来るものには役立ちません。

それが真に一定であれば、DEPSでそれを指定すると、傷つけることはありません。 カスタムフック内のsetState関数がコンポーネントに返され、エフェクトから呼び出す場合などです。 lintルールは、このような間接参照を理解するのに十分なほど賢くはありません。 しかし一方で、誰でも後で戻る前にそのコールバックをラップし、おそらくその中の別の小道具や状態を参照することができます。 そうすると、それは一定ではなくなります! そして、これらの変更を処理できないと、厄介な古い小道具/状態のバグが発生します。 したがって、それを指定する方が適切なデフォルトです。

ただし、関数値が必然的に一定であるというのは誤解です。 メソッドのバインドにより、クラス内で一定であることがよくありますが、それによって独自の範囲のバグが発生します。 ただし、一般に、関数コンポーネントの値を閉じる関数は、定数とは見なされません。 lintルールは、何をすべきかを指示することについてより賢くなりました。 (エフェクト内に移動する(最も簡単な修正)、またはuseCallbackラップするなど。)

これの反対のスペクトルに問題があります。これは、無限ループが発生する場所です(関数値は常に変化します)。 可能な場合は(同じコンポーネントで)lintルールでそれをキャッチし、修正を提案します。 しかし、何かを数レベル下に渡すと注意が必要です。

それでも問題を修正するためにuseCallbackラップすることができます。 技術的には、関数を変更することは有効であり、バグのリスクを冒さずにこのケースを無視することはできないonChange={shouldHandle ? handleChange : null}や、同じ場所でのfoo ? <Page fetch={fetchComments /> : <Page fetch={fetchArticles />レンダリングなど。 または、親コンポーネントの状態を閉じるfetchCommentsですらあります。 それは変わるuseCallbackと、関数のID自体が変更されますが、必要な場合に限ります。 したがって、これは便利なプロパティであり、回避するための障害ではありません。

無限の非同期ループを検出するためのより良いソリューションを追加する必要があります。 これにより、最も紛らわしい側面が緩和されるはずです。 将来、これに対する検出を追加する可能性があります。 次のようなものを自分で書くこともできます。

useWarnAboutTooFrequentChanges([deps]);

これは理想的ではなく、これをより優雅に処理することを検討する必要があります。 私はこのようなケースがかなり厄介であることに同意します。 ルールに違反しない修正は、たとえばAPIをcreateTextInput(rules)に変更して、 rules静的にし、 registerとunregisterをuseCallbackにラップすることです。 registerとunregister削除し、 dispatchだけを配置する別のコンテキストに置き換えます。 そうすれば、それを読むこととは異なる関数IDを持つことが決してないことを保証できます。

とにかくコンテキスト値にuseMemoを追加することをお勧めします。これは、プロバイダーが多くの計算を行うため、新しいコンポーネントが登録されていないが、自身の親が更新された場合に繰り返すのは悲しいことです。 したがって、この種の問題は、他の方法では気付かなかったかもしれない問題をより目立たせます。 私は同意しますが、これが起こったときにそれをさらに目立たせる必要があります。

関数従属性を完全に無視すると、関数コンポーネントとフックのバグが悪化します。これを行うと、古い小道具と状態が表示され続けるためです。 ですから、できる限りそうしないようにしてください。

複合値の変化に反応する

この例が本質的にイベントハンドラーである何かにエフェクトを使用する理由は私には奇妙です。 イベントハンドラーで同じ「ログ」(フォーム送信の可能性があると思います)を実行する方が適切なようです。 これは、コンポーネントがアンマウントされたときに何が起こるかを考える場合に特に当てはまります。 エフェクトがスケジュールされた直後にアンマウントした場合はどうなりますか? その場合、フォーム送信のようなものは単に「起こらない」べきではありません。 ですから、効果は間違った選択かもしれないようです。

つまり、 fullNameをsetSubmittedData({firstName, lastName})にすると、 [submittedData]が依存関係になり、そこからfirstName読み取ることができます。 lastName 。

命令型/レガシーコードとの統合

jQueryプラグインや生のDOMAPIなどの必須のものと統合する場合、多少の不快感が予想される場合があります。 そうは言っても、その例では、効果をもう少し統合できると期待しています。


誰も忘れなかったといいのですが! 私がやったのか、何か不明な点があるのか​​教えてください。 これからのレッスンをすぐにいくつかのドキュメントに変えようとします。

@gaearon 、時間を割いて問題を(@trevorgithubによる#14920(コメント))を見逃しました。 (多くの方からのフィードバックが多かったことは確かにありがたいです。私の元のコメントは、問題のコメントの途中の隠しアイテムのセクションで失われたと思います)。

私のサンプルは「命令型/レガシーコードとの統合」に該当すると思いますが、他のカテゴリも同様ですか?

「命令型/レガシーコードとの統合」の問題の場合、実行できることが多くないように思われます。 そのような場合、この警告をどのように無視しますか? 私は推測する:
// eslint-disable-line react-hooks/exhaustive-deps

すみません、これを逃しました。

小道具を介してデータを受信したが、明示的な変更が行われるまでそれらの小道具を使用したくない場合は、状態を派生させることをモデル化する正しい方法のように思えます。

あなたはそれを「別の小道具まで小道具への変更を無視したい」と考えています。 しかし、「私のコンポーネントには、状態でフェッチ機能があります。 別の小道具が変更されると、小道具から更新されます。」

一般的に派生状態は推奨されませんが、ここではあなたが望むもののようです。 これを実装する最も簡単な方法は次のようになります。

const [currentGetData, setCurrentGetData] = useState(getData);
const [prevRefreshRequest, setPrevRefreshRequest] = useState(refreshRequest);

if (prevRefreshRequest !== refreshRequest) {
  setPrevRefreshRequest(refreshRequest);
  setCurrentGetData(getData);
}

useEffect(() => {
  currentGetData(someId);
}, [currentGetData, someId]);

また、配置する必要があると思いますuseCallbackの周りにgetDataあなたがダウンして渡していること。

一般に、フェッチのために非同期関数を渡すパターンは、私には怪しげに思えることに注意してください。 あなたの場合、Reduxを使用していると思いますので、それは理にかなっています。 ただし、非同期関数が親コンポーネントで定義されている場合は、競合状態になる可能性があるため、疑わしいものになります。 エフェクトにはクリーンアップがないので、ユーザーが別のIDを選択したことをどのようにして知ることができますか? リクエストが順不同で到着し、間違った状態を設定するリスクがあります。 ですから、それは覚えておくべきことです。 データフェッチがコンポーネント自体にある場合は、「無視」フラグを設定してsetStateが応答しないようにするエフェクトクリーンアップ関数を使用できます。 (もちろん、データを外部キャッシュに移動する方が優れたソリューションであることがよくあります。これがSuspenseの機能でもあります。)

イベントハンドラーで同じ「ログ」(フォーム送信の可能性があると思います)を実行する方が適切なようです。

@gaearon私が見る問題は、イベントハンドラーでそれを行うことは、更新がコミットされる前に副作用が発生することを意味するということです。 そのイベントの結果としてコンポーネントが正常に再レンダリングされるという厳密な保証はないため、イベントハンドラーでそれを行うのは時期尚早である可能性があります。

たとえば、ユーザーが新しい検索クエリを正常に送信し、結果を表示していることをログに記録したい場合です。 何か問題が発生してコンポーネントがスローされた場合、そのログイベントが発生することは望ましくありません。

次に、この副作用が非同期である可能性があるため、 useEffectを使用するとクリーンアップ関数が得られます。

useReducerの問題もあります。この場合、ログに記録したい値がイベントハンドラーで使用できません。 しかし、私はそれがすでにすべてのレーダーにあると思います🙂

どちらの場合も、推奨するアプローチで十分である可能性があります。 構成された状態を、構成された個々の値に引き続きアクセスできる形式で保存します。

関数を追加のパラメーターでラップして渡すための便利なフックがあります。 これは次のようになります。

function useBoundCallback(fn, ...bound) {
  return useCallback((...args) => fn(...bound, ...args), [fn, ...bound]);
}

(いくつかの追加機能があるため、実際にはもう少し複雑ですが、上記は関連する問題を示しています)。

ユースケースは、コンポーネントがプロパティを子に渡す必要があり、子にその特定のプロパティを変更する方法を持たせたい場合です。 たとえば、各アイテムに編集オプションがあるリストについて考えてみます。 親オブジェクトは各子に値を渡し、値を編集する場合はコールバック関数を呼び出します。 子は表示されているアイテムIDを知らないため、親はこのパラメーターを含めるようにコールバックを変更する必要があります。 これは、任意の深さにネストできます。

このフローの簡単な例を次に示します: https :


問題は、このルールで2つのリンターエラーが発生することです。

React Hook(X)に依存関係がありません: 'bound'。 それを含めるか、依存関係配列を削除します

React Hook(X)の依存関係配列にはspread要素があります。 これは、正しい依存関係を渡したかどうかを静的に検証できないことを意味します

パラメータのリストを使用するように変更すると(つまり、スプレッド演算子を使用しないと)、パラメータが同一で​​あっても、呼び出しごとにリストが作成されるため、メモ化が破棄されます。


考え:

  • 2番目のエラーも当てはまる場合、最初のエラーを表示する価値はありますか?
  • スプレッド演算子が使用されている場所でこのルールを無効にする方法はありますか?
  • 関数内で変数を使用するのがspread演算子のみである場合、依存関係でspread演算子を使用しても安全です。ルールはすでにこれを検出しているので、確実に許可する必要があります。 静的分析では解決が難しい、より複雑なケースがあることはわかっていますが、これは、比較的一般的なスプレッドの使用にとっては簡単な勝利のようです。

こんにちは@gaearon 、私はちょうどこの警告を

Accessing 'myRef.current' during the effect cleanup will likely read a different ref value because by this time React has already updated the ref. If this ref is managed by React, store 'myRef.current' in a variable inside the effect itself and refer to that variable from the cleanup function.

このメッセージは少し紛らわしいと思います。 クリーンアップ時の参照電流値がエフェクト本体の値と異なる可能性があることを警告しようとしていると思います。 右?
その場合、それを認識している場合、この警告を無視しても安全/合法ですか?

私の場合、興味深い場合: CodeSandbox
コンテキスト:いくつかのカスタムデータフェッチフック。 競合状態とマウントされていないコンポーネントの更新の両方を防ぐために、refでカウンターを使用します。

関数内で読み取られた参照を非表示にするか、クリーンアップの場合に別のブール参照を作成することで、この警告を回避できると思います。 しかし、この警告を無視することができれば、不必要に冗長になります。

@aweary

たとえば、ユーザーが新しい検索クエリを正常に送信し、結果を表示していることをログに記録したい場合です。 何か問題が発生してコンポーネントがスローされた場合、そのログイベントが発生することは望ましくありません。

ええ、それはニッチなユースケースのように聞こえます。 ほとんどの人が「副作用」を望んでいるとき、それはフォームの送信自体を意味していると思います。送信されたフォームを表示したという事実ではありません。 その場合、私が提供した解決策は問題ないようです。

@ davidje13

2番目のエラーも当てはまる場合、最初のエラーを表示する価値はありますか?

lintルールを変更する提案については、新しい問題を提出してください。

スプレッド演算子が使用されている場所でこのルールを無効にする方法はありますか?

自分が何をしているのかを知っていると思うなら、いつでも// eslint-disable-next-line react-hooks/exhaustive-depsできます。

関数内で変数を使用するのがspread演算子のみである場合、依存関係でspread演算子を使用しても安全です。ルールはすでにこれを検出しているので、確実に許可する必要があります。

新しい問題のplsを提出してください。

@CarlosGines

このメッセージは少し紛らわしいと思います。 クリーンアップ時の参照電流値がエフェクト本体の値と異なる可能性があることを警告しようとしていると思います。 右?

はい。

その場合、それを認識している場合、この警告を無視しても安全/合法ですか?

うーん..それがバグにつながる場合は違います。 🙂

コンテキスト:いくつかのカスタムデータフェッチフック。 競合状態とマウントされていないコンポーネントの更新の両方を防ぐために、refでカウンターを使用します。

ええ、多分このユースケースは合法です。 plsを議論するために新しい問題を提出しますか?

十分なフィードバックがあり、組み込まれているので、この問題をロックします。

一般的な質問と回答: https :

useEffectと依存関係について詳しく知りたい場合は、 https :

間もなくドキュメントに追加する予定です。

ルールの内容を変更したい場合、またはケースが正当であるかどうかわからない場合は、新しい問題を提出してください。

このページは役に立ちましたか?
0 / 5 - 0 評価