useEffectフック内で状態を設定できますか


122

他の状態に依存している状態があるとしましょう(たとえば、Aが変更されたときにBを変更したい)。

Aを監視し、useEffectフック内にBを設定するフックを作成することは適切ですか?

ボタンをクリックすると、最初のエフェクトが起動してbが変化し、次のレンダリングの前に2番目のエフェクトが起動するように、エフェクトはカスケードされますか?このようなコードを構造化することのパフォーマンス上の欠点はありますか?

let MyComponent = props => {
  let [a, setA] = useState(1)
  let [b, setB] = useState(2)
  useEffect(
    () => {
      if (/*some stuff is true*/) {
        setB(3)
      }
    },
    [a],
  )
  useEffect(
    () => {
      // do some stuff
    },
    [b],
  )

  return (
    <button
      onClick={() => {
        setA(5)
      }}
    >
      click me
    </button>
  )
}

回答:


32

あるエフェクト内でSetStateを設定した場合でも、エフェクトは常にレンダリングフェーズの完了後に実行され、別のエフェクトは更新された状態を読み取り、レンダリングフェーズの後でのみアクションを実行します。

bそれ以外の理由で変更される可能性がない限り、両方のアクションを同じ効果で実行する方がおそらく良いと言っていchanging aますが、その場合も同じロジックを実行する必要があります


4
では、AがBを変更した場合、コンポーネントは2回正しくレンダリングされますか?
ダンラズウィック2018

2
私はまた、上記の質問に対する答えを知りたい
alaboudi

2
@alaboudiはい、Aが変更されてuseeffectが実行され、Bが設定された場合、コンポーネントは2回レンダリングされます
ShubhamKhatri20年

@alaboudiはい..ShubhamKhatriが再びレンダリングすると言ったように。しかし、あなたは参照してください2番目の引数を使用して再レンダリングした後、あなたの効果を呼び出すスキップすることができreactjs.org/docs/...
カーティケヤン

106

一般的に言って、setStateinsideuseEffectを使用すると、発生させたくない可能性が最も高い無限ループが作成されます。そのルールにはいくつかの例外がありますが、これについては後で説明します。

useEffectは各レンダリングの後に呼び出され、そのsetState内部で使用されると、コンポーネントが再レンダリングされ、呼び出しuseEffectなどが行われます。

useStateinsideを使用してもuseEffect無限ループが発生しない一般的なケースの1つは、useEffectlikeの2番目の引数として空の配列を渡す場合です。useEffect(() => {....}, [])これは、最初のマウント/レンダリングの後でのみ、エフェクト関数を1回呼び出す必要があることを意味します。これは、コンポーネントでデータフェッチを実行していて、リクエストデータをコンポーネントの状態で保存する場合に広く使用されます。


3
setState内部useEffectで使用するもう1つのケースは、setting stateサブスクリプションまたはイベントリスナーの内部です。ただし、サブスクリプションをキャンセルすることを忘れないでください 。reactjs.org
docs /

35
これは厳密には当てはまりません。useStateは、状態を更新する値が前の値と異なる場合にのみ発生するため、サイクル間で値が変更されない限り、無限ループが防止されます。
RobM

14
この答えは正しくなく、質問のポイントではありません。コードでは、ボタンがクリックされたときにコンポーネントが2回だけレンダリングされ、無限ループはありません
BogdanD19年

15
useEffect内で状態を設定することは非常に一般的なユースケースです。データの読み込み、useEffectがAPIを呼び出し、データを取得し、useStateのset部分を使用して設定することを考えてください。
badbod 9919

3
あなたが言及した問題に対処するために答えを更新しました、それは今より良いですか?
HossamMourad19年

58

将来の目的のために、これも役立つかもしれません:

useEffectループを作成しないように、すでに説明したように注意を払う必要があるだけで、setStateを使用しても問題ありません。

しかし、発生する可能性のある問題はそれだけではありません。下記参照:

親からComp受信するコンポーネントがありpropsprops変更に応じてCompの状態を設定するとします。何らかの理由で、異なる小道具ごとに変更する必要がありますuseEffect

こんなことしないで

useEffect(() => {
  setState({ ...state, a: props.a });
}, [props.a]);

useEffect(() => {
  setState({ ...state, b: props.b });
}, [props.b]);

この例でわかるように、の状態が変わることはありません:https//codesandbox.io/s/confident-lederberg-dtx7w

これがため、この例では、それはだ起こる理由の両方useEffectsが同じで実行サイクルを反応させ、あなたが両方を変更したときprop.aprop.bの値ので、{...state}あなたがないときsetStateの両方でまったく同じですuseEffect彼らは同じ状況にあるため。2番目を実行するsetStateと、最初のが置き換えられsetStateます。

これを代わりに行う

この問題の解決策は、基本的に次のsetStateように呼び出すことです。

useEffect(() => {
  setState(state => ({ ...state, a: props.a }));
}, [props.a]);

useEffect(() => {
  setState(state => ({ ...state, b: props.b }));
}, [props.b]);

ここで解決策を確認してください:https//codesandbox.io/s/mutable-surf-nynlx

これで、を続行すると、常に最新の正しい状態の値を受け取りますsetState

これが誰かに役立つことを願っています!


3
上記の解決策は私を助けましたsetName(name => ({ ...name, a: props.a }));
mufaddal_mw

2
これは、矢印機能の部分でも私を助けましたsetItems(items => [...items, item])
StefanZhelyazkov20年

うわー、あなたは素晴らしいです。あなたは今日私のa * sを救った。
PratikSoni20年

解決策がないまま午前7時から午後3時まで過ごしたので、あなたは私を救ってくれました。
ディニンドゥカンチャナ

23

useEffect特定の小道具や状態に引っ掛かることができます。したがって、無限ループフックを回避するために必要なことは、いくつかの変数または状態をバインドして実行することです。

例えば:

useEffect(myeffectCallback, [])

上記の効果は、コンポーネントがレンダリングされた後にのみ発生します。これはcomponentDidMountライフサイクルに似ています

const [something, setSomething] = withState(0)
const [myState, setMyState] = withState(0)
useEffect(() => {
  setSomething(0)
}, myState)

上記の効果は、私の状態が変更された場合にのみcomponentDidUpdate発生します。これは、すべての変化する状態が発生するわけではないことを除いて、同様です。

このリンクから詳細を読むことができます


1
ありがとうございます。この回答は、他の回答では不可能だった方法で、useEffectの依存関係配列に対応しています。useEffectの2番目の引数として空の配列を含めると、コンポーネントがレンダリングされた後にuseEffectが確実に実行されますが、特定の状態または特定の状態の配列を含めると、参照の状態が変更されたときにuseEffectが実行されます。
adriaanbd

11

▶1。useEffectフック内で状態を設定できますか?

原則として、状態は必要な場所に自由に設定できます。内部useEffectレンダリング中も含まれます。フックをdeps適切に設定したり、条件付きで状態を設定したりして、無限ループを回避するようにしてください。


▶2。他の状態に依存している状態があるとしましょう。Aを監視し、useEffectフック内にBを設定するフックを作成することは適切ですか?

あなたはちょうど古典的なユースケースを説明ましたuseReducer

useReducer通常、複数のサブ値を含む複雑な状態ロジックがある場合、または次の状態が前の状態に依存してuseStateいる場合よりも望ましいです。(Reactドキュメント

状態変数の設定が別の状態変数の現在の値に依存している場合は、両方をに置き換えてみてください。[...]自分で書いていることに気付いたら、代わりにレデューサーの使用を検討する良い機会です。(ダン・アブラモフ、過剰反応ブログuseReducer setSomething(something => ...)


▶3。ボタンをクリックすると、最初のエフェクトが起動してbが変化し、次のレンダリングの前に2番目のエフェクトが起動するように、エフェクトがカスケードされますか?

useEffectレンダリングがコミットされ、DOMの変更が適用された後に常に実行されます。最初のエフェクトは発火し、変化bし、再レンダリングを引き起こします。このレンダリングが完了すると、b変更により2番目のエフェクトが実行されます。


▶4。このようなコードを構造化することによるパフォーマンスの欠点はありますか?

はい。の状態変化をb別のuseEffectforでラップすることによりa、ブラウザには追加のレイアウト/ペイントフェーズがあります-これらの効果は潜在的にユーザーに表示されます。useReducer試してみたい方法がない場合はba直接一緒に状態を変更できます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.