繁体   English   中英

“React 检测到 Hooks 的顺序发生了变化”但是 Hooks 似乎是按顺序调用的

[英]"React has detected a change in the order of Hooks" but Hooks seem to be invoked in order

我试图通过 React 的钩子使用ContextReducers ,但遇到了钩子顺序不固定的问题。 我的理解是,只要useHook(…)的顺序保持不变,就可以在任何类型的控制流中调用返回的状态/更新函数/reducer。 否则,我将在 FunctionComponents 的最开始调用挂钩。

是我在循环中生成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-dnduseDrop的引用。

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…
    // …
  })
  // …
}

由于使用了短路逻辑,我在编写的组件中遇到了同样的错误消息。

这导致了一个错误:

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 ; }

写下我的评论作为答案:

问题是你直接调用Event.Event() ,即使它是一个反应组件。 这导致 react 将函数内部的钩子调用视为Container一部分,即使您打算将它们作为 Event 的一部分。

解决方案是使用 JSX:

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

当您用生成的 JS 代码替换 JSX 时,为什么会更清楚:

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

请参阅https://reactjs.org/docs/react-api.html#createelement 你永远不想直接调用函数组件,react 的工作原理是你总是传递一个引用来响应组件,让它在正确的时间调用函数。

由于其他原因而不是这个问题,当您收到此error

在此处输入图片说明

它实际上是由于钩子实现的任何不良做法而发生的

1 - 仅在顶层调用挂钩

不要在循环条件嵌套函数中调用 Hook。 相反,始终在 React 函数的顶层使用 Hooks

注意:首先在函数顶部实现你的useState钩子

2 - 仅从 React 函数调用钩子

不要从常规 JavaScript 函数调用 Hook

3 - 在测试过程中出错

如果在测试组件时遇到此错误,请注意设置自定义钩子的位置(替换到函数顶部)

最佳实践

使用eslint对你的代码进行 lint 避免出现React Hooks Rules错误

使用 npm 或 yarn 安装包

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

这不是问题场景,而是错误本身,希望它会帮助某人:)

  const { chatSession, userBelongsToSession } = useChatSession(session_id)
  const { activeSession, setActiveSession } = useActiveChatSession()

  const isCurrentActiveSession = useMemo(() => activeSession != null && activeSession.session_id === session_id, [activeSession, session_id])

  if (chatSession == null || activeSession == null) {
    return (<div></div>)
  }

  const Container = styled(BorderedContainer)`height: 72px; cursor: pointer;`

我在这段代码中发生了同样的错误,并且与样式组件调用 useRef 相关,并且之前由于条件渲染而未调用

if (chatSession == null || activeSession == null) {
  return (<div></div>)
}

默认情况下,钩子将返回 null 并且我的组件将在不使用 useRef 的情况下呈现,尽管当钩子实际填充时,样式组件将生成一个带有 useRef 的组件。

  if (chatSession == null || activeSession == null) {
    return (<div></div>)
  }

const Container = styled(BorderedContainer)`height: 72px; cursor: pointer;

我在 react-native 应用程序中也遇到了同样的问题。 这是由于不必要的元素加载过程而发生的。

旧代码

return (......
     <NotificationDialog
          show={notificationPayload !== null}
          title={notificationPayload?.title}
          .......
        />
);

这将重新渲染 NotificationDialog 甚至 notificationPayload !== null。 但它不是必需的。 简单地我添加了这个空检查并避免呈现这个 NotificationDialog 如果它为空

使固定

  return (......
         { notificationPayload !== null? <NotificationDialog
              show={notificationPayload !== null}
              title={notificationPayload?.title}
              .......
            /> : null } 
    );

在我编写的测试中调用不同的钩子方法时,我随机收到此错误。 对我的修复是在我实施的useRef间谍中:

const useRefSpy = jest
  .spyOn(React, 'useRef')
  .mockReturnValueOnce({ whatever })

mockReturnValueOnce更改为mockReturnValue修复了错误。

从 useEffect 调用多个 api 调用

就我而言,我正在执行多个 api 调用并将它们中的每一个保存到不同的状态,这导致我出现此错误。 但是在将其更改为单个状态后,我能够克服它。

const [resp, setGitData] = useState({ data: null, repos: null });

  useEffect(() => {
    const fetchData = async () => {
      const respGlobal = await axios(
        `https://api.github.com/users/${username}`
      );
      const respRepos = await axios(
        `https://api.github.com/users/${username}/repos`
      );

      setGitData({ data: respGlobal.data, repos: respGlobal.data });
    };

    fetchData();
  }, []);

我通过将导入的组件用作组件而不是 function 解决了我的问题,我不得不返回 jsx ( <>{values}</> ) 而不仅仅是值,因为我必须在 ConvertDate 中导入 redux 的 useSelector,显然它没有如果它只是一个 function,则不能使用 useSelector。我可以只使用 function 并将日期和 redux 的 state 作为道具传递...

我有什么:

<b>{ConvertDate({ date: post.dateTime })}</b>

以及我如何修复它:

<b>{<ConvertDate date={post.dateTime} />}</b>

我知道我来晚了,但只是想分享我在同一个错误堆栈上的经验。

ReactJS 17 中,tanstack 的useQuery 4 和useEffect的组合发生在我身上。

这里有一场精彩的讨论。

堆栈跟踪:

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

   Previous render            Next render
   ------------------------------------------------------
1. useContext                 useContext
2. useContext                 useContext
3. useState                   useState
4. useRef                     useRef
5. useEffect                  useEffect
6. useRef                     useRef
7. useEffect                  useEffect
8. useContext                 useContext
9. useState                   useState
10. useRef                    useRef
11. useEffect                 useEffect
12. useRef                    useRef
13. useEffect                 useEffect
14. useState                  useState
15. useState                  useState
16. useState                  useState
17. useState                  useState
18. useState                  useState
19. useState                  useState
20. useContext                useContext
21. useContext                useContext
22. useContext                useContext
23. useContext                useContext
24. useEffect                 useEffect
25. useState                  useState
26. useCallback               useCallback
27. useState                  useState
28. useLayoutEffect           useLayoutEffect
29. useEffect                 useEffect
30. useDebugValue             useDebugValue
31. useEffect                 useEffect
32. undefined                 useEffect
   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

所以它正在发生,因为我在useQuery api 回调行之后使用了useEffect ,我通过将useEffect放在useQuery行之前解决了这个问题,并且所有警告堆栈跟踪都得到了解决。

希望它能帮助别人。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM