React 自定义钩子吐出错误“错误:应该有一个队列。这可能是 React 中的错误。请提交问题”

Edw*_*Sun 1 javascript reactjs react-hooks react-state

我有一个反应组件,需要从组件内使用自定义的反应挂钩。

但是,只有在启用功能切换时才应调用此挂钩。我知道这是一种反模式,因为它违反了这里的钩子规则: https: //reactjs.org/docs/hooks-rules.html

所以我的组件文件大致是这样的结构:

    const someFeatureToggle = useSomeFeatureToggleHook(React);
    const callBackMethod = ()=>{
        // doing the logic
     }
    const someRef1 = React.useRef();
    const someOtherRef = React.useRef();
    ...
    There are lots of useState( ) here

    return (
        JSX
      )

Run Code Online (Sandbox Code Playgroud)

对于定制的钩子:

export default function myCustomizedHook(topics, messagesReceivedFn, subscriptionOptions = {}) {
  if (!isValidTopics(topics)) {
    throw new Error(`Topics arg is invalid - Arg ${JSON.stringify(topics)}`);
  }
  const [someSubTopics] = useState([topics].flat());

  const context = useContext(SomeEventContext);
  if (isUndefined(context)) {
    throw new Error(`${customizedHook.name} must be used within SomeProvider`);
  }
  const { connectionStatus, connector } = context;
  const isConnectorConnected = connector?.connected ?? false;
  const isConnectorReconnecting = connector?.reconnecting ?? false;

  const messageReceivedHandler = useCallback(
    (receivedTopic, message) => {
      if (subscribedTopics.some((topic) => matches(topic, receivedTopic))) {
        messagesReceivedFn?.(receivedTopic, message);
      }
    },
    [messagesReceivedFn, subscribedTopics]
  );

  useEffect(() => {
    isConnectorConnected && connector?.on(CLIENT_EVENTS.MESSAGE, messageReceivedHandler);

    return () => {
      connector?.off(CLIENT_EVENTS.MESSAGE, messageReceivedHandler);
    };
  }, [messageReceivedHandler, connector, isConnectorConnected]);

  useDeepCompareEffect(() => {
    isConnectorConnected && connector.subscribe(subscribedTopics, subscriptionOptions);

    return () => {
      subscribedTopics && connector?.unsubscribe(subscribedTopics);
    };
  }, [connector, isConnectorConnected, subscribedTopics, subscriptionOptions]);

  return { isConnected: isConnectorConnected, isReconnecting: isConnectorReconnecting, connectionStatus, subscribedTopics };
Run Code Online (Sandbox Code Playgroud)

现在错误跟踪是这样的:

Uncaught Error: Should have a queue. This is likely a bug in React. Please file an issue.
    at updateReducer (react-dom.development.js:15255:15)
    at updateState (react-dom.development.js:15671:12)
    at Object.useState (react-dom.development.js:16472:18)
    at useState (react.development.js:1533:23)
    at customizedHook (customizedHook.js:28:38)
    at componentThatConsumeHook (componentThatConsumeHook.js:67:99)
    at renderWithHooks (react-dom.development.js:15015:20)
    at updateFunctionComponent (react-dom.development.js:17386:22)
    at beginWork (react-dom.development.js:19093:18)
    at HTMLUnknownElement.callCallback (react-dom.development.js:3942:16)
Run Code Online (Sandbox Code Playgroud)

并且浏览器中的开发控制台有此警告:

Warning: React has detected a change in the order of Hooks called by myComponent. 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. useRef                     useRef
2. useState                   useState
3. useEffect                  useEffect
4. useRef                     useState
   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Run Code Online (Sandbox Code Playgroud)

customizedHook 的第 28 行指向下面这一行:

const [someSubTopics] = useState([topics].flat());
Run Code Online (Sandbox Code Playgroud)

这就是我在组件中使用钩子的方式:

const result = (!!featureToggles.Flag) && customHook(arg1, callbackMethod);
Run Code Online (Sandbox Code Playgroud)

我在这里摸不着头脑,因为我不知道为什么会发生这种情况,任何想法或建议将不胜感激!

Vu *_*ong 5

那是因为你在条件内调用 hook 。

\n
const result = (!!featureToggles.Flag) && customHook(arg1, callbackMethod);\n
Run Code Online (Sandbox Code Playgroud)\n

基本上是一样的

\n
let result = false\nif (!!featureToggles.Flag){\n  result = customHook(arg1, callbackMethod);\n}\n
Run Code Online (Sandbox Code Playgroud)\n

来自React文档:

\n
\n

不要\xe2\x80\x99t 在循环、条件或嵌套函数内调用 Hook。

\n
\n

您可以做的就是传递featureToggles到您的自定义挂钩并在其中进行检查。

\n

确保您的条件检查位于钩子的底部,位于每个钩子调用的下方。

\n
// pass featureToggles here\nexport default function myCustomizedHook(featureToggles, topics, messagesReceivedFn, subscriptionOptions = {}) {\n  // your code here\n\n  useDeepCompareEffect(() => {\n    isConnectorConnected && connector.subscribe(subscribedTopics, subscriptionOptions);\n\n    return () => {\n      subscribedTopics && connector?.unsubscribe(subscribedTopics);\n    };\n  }, [connector, isConnectorConnected, subscribedTopics, subscriptionOptions]);\n\n  // make sure to do the check at the bottom, below every hook call\n  if (!!featureToggles.Flag) {\n    // handle this feature toggle\n    return null\n  }\n\n  return { isConnected: isConnectorConnected, isReconnecting: isConnectorReconnecting, connectionStatus, subscribedTopics };\n
Run Code Online (Sandbox Code Playgroud)\n