ウェブサイト検索

javascript - 「Reactはフックの順序の変更を検出しました」と表示されますが、フックは順序通りに呼び出されているようです


私はReactのフックを使ってContextとReducersを利用しようとしており、フックの順序が一定でないことで問題に直面しています。私の理解では、useHook(…)の順序が同じであれば、返された状態/更新関数/リデューサーをどのような制御フローで呼び出しても問題ないはずでした。そうでなければ、FunctionComponentの最初でフックを呼び出しています。

私がループでDaysを生成しているからですか?それとも他に何か見落としていますか?

Warning: React has detected a change in the order of Hooks
called by Container. This will lead to bugs and errors if not fixed. For
more information, read the Rules of Hooks:
https://reactjs.org/docs/hooks-rules.html

   Previous render            Next render
   ------------------------------------------------------
1. useContext                 useContext
2. undefined                  useRef
   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

以下はContainerのフルバージョンです。Dayの抜粋は以下の通りで、react-dndのuseDropからの参照があります。

export const Container: FunctionComponent<Props> = () => {
  let events = useContext(State.StateContext)
  //let events: Array<Event.Event> = [] <- with this, no warning

  const getDaysEvents = (day: Event.Time, events: Array<Event.Event>) => {
    return events.map(e => {
      const isTodays = e.startTime.hasSame(day, "day")
      return isTodays && Event.Event({ dayHeight, event: e })
    })
  }

  let days = []
  for (let i = 0; i < 7; i++) {
    const day = DateTime.today().plus({ days: i })
    days.push(
      <Day key={day.toISO()} height={dayHeight} date={day}>
        {getDaysEvents(day, events)}
      </Day>
    )
  }
  return <div className="Container">{days}</div>
}

Day からの抜粋(Event も同様に useDrag フックを使用しており、ここでのようにトップレベルで呼び出されます)。

const Day: FunctionComponent<DayProps> = ({ date, height, children }) => {
  const dispatch = useContext(State.DispatchContext)
  const [{ isOver, offset }, dropRef] = useDrop({
    // …uses the dispatch function within…
    // …
  })
  // …
}

回答 #1

私は、短絡論理の使用のために書いていたコンポーネントで同じエラーメッセージに遭遇しました。

これによりエラーが発生しました:

const x = useSelector(state => state.foo);
if (!x) { return ; }
const y = useSelector(state => state.bar);

これは、x が真の時にはフックのリストの長さが2になるのに対し、x が偽の時にはリストの長さが1になるためです。

エラーを解決するために、私はすべてのフックの使用を早期終了より前に置かなければなりませんでした。

const x = useSelector(state => state.foo);
const y = useSelector(state => state.bar);
if (!x) { return ; }

回答 #2

コメントを回答として書きます:

問題は、Event.Event() を直接呼び出していることです。これは React コンポーネントであるにもかかわらずです。そのため、関数内のフック呼び出しを React が Container の一部として扱ってしまい、あなたの意図では Event の一部として扱われるべきものになっています。

解決策はJSXを使用することです:

return isTodays && <Event.Event dayHeight={dayHeight} event={e} />

これが機能する理由は、JSXを生成されたJSコードに置き換えるとより明確になります。

return isTodays && React.createElement(Event.Event, { dayHeight, event: e })

https://reactjs.org/docs/react-api.html#createelement を参照してください。関数コンポーネントを直接呼び出すことは決してありません。React の動作方法は、常にコンポーネントへの参照を React に渡し、適切なタイミングで関数を呼び出させることです。

回答 #3

この質問の代わりに他の理由で、このエラーを受け取ったとき

これは実際には、フックの実装における悪い習慣が原因で起こります

1 - フックはトップレベルでのみ呼び出す

Hooksをループ、条件、またはネストされた関数の中で呼び出さないでください。代わりに、常にReact関数のトップレベルでHooksを使用してください。

注意:関数の先頭でまずuseStateフックを実装してください

2 - React関数からのみフックを呼び出す

通常のJavaScript関数からHooksを呼び出さないでください

3 - テスト中にエラーが発生する

コンポーネントをテストしているときにこのエラーが出た場合、カスタムフックをどこに置くかに注意してください(関数の先頭に置き換えてください)

最良の方法

コードのリンティングに eslint を使用して、React Hooks ルールのエラーを避ける

npm または yarn でパッケージをインストールする

npm install eslint-plugin-react-hooks --save-dev

関連記事