web-dev-qa-db-ja.com

一定の条件に基づいて反応フックを呼び出すことは安全ですか?

Rules of Hooks では、すべてのレンダリングで同じフックが同じ順序で呼び出される必要があります。そして、あなたがこのルールを破ると何がうまくいかないのかについての説明があります。たとえば、次のコード:

function App() {
  console.log('render');
  const [flag, setFlag] = useState(true);
  const [first] = useState('first');
  console.log('first is', first);
  if (flag) {
    const [second] = useState('second');
    console.log('second is', second);
  }
  const [third] = useState('third');
  console.log('third is', third);

  useEffect(() => setFlag(false), []);

  return null;
}

コンソールへの出力

render 
first is first 
second is second 
third is third 
render 
first is first 
third is second 

また、警告またはエラーが発生します。

しかし、要素のライフサイクル中に変化しない条件についてはどうでしょうか?

const DEBUG = true;

function TestConst() {
  if (DEBUG) {
    useEffect(() => console.log('rendered'));
  }

  return <span>test</span>;
}

このコードは実際にはルールに違反しておらず、正常に動作しているようです。しかし、それでもeslint警告がトリガーされます。

さらに、小道具に基づいて同様のコードを書くことが可能であるようです:

function TestState({id, debug}) {
  const [isDebug] = useState(debug);

  if (isDebug) {
    useEffect(() => console.log('rendered', id));
  }

  return <span>{id}</span>;
}

function App() {
  const [counter, setCounter] = useState(0);
  useEffect(() => setCounter(1), []);
  return (
    <div>
      <TestState id="1" debug={false}/>
      <TestState id="2" debug={true}/>
    </div>
  );
}

このコードは意図したとおりに機能します。

それで、変更されないと確信しているときに、条件内でフックを呼び出すことは安全ですか?そのような状況を認識するようにeslintルールを変更することは可能ですか?

問題は、実際の要件に関するものであり、同様の動作を実装する方法ではありません。私が理解している限り、

コンポーネントがレンダリングされるたびにフックが同じ順序で呼び出されるようにします。 Reactが複数のuseStateとuseEffectの呼び出しの間でフックの状態を正しく保存することを可能にするのは、このためです。

そして、このルールの例外の場所があります:「ループ、条件、またはネストされた関数内でフックを呼び出さないでください」。

10
UjinT34

このパターンは使用しないでください。これはあなたの例ではうまくいくかもしれませんが、ニース(または慣用的)ではありません。

標準的なパターン(正当な理由による)は、初期状態がコンストラクターで宣言され、本体(setState)の条件に応じて更新されることです。 Reactフックはステートレスコンポーネントのこの機能をミラーリングしているため、同じように動作するはずです。

次に、この状態の一部を動的に追加し、後でレンダリングの問題を引き起こす可能性があることを理解できません。あなたの例では、単純なconstも同様に機能します-動的状態を使用する理由はありません。

このことを考慮:

return (<React.Fragment>{second}</React.Fragment>)

これは、secondを定義していない場合は常に、参照エラーで中断します。

0
jsdeveloper