React custom hook spit Error " Error: Should have a queue. This is likely a bug in React. Please file an issue"

React custom hook spit Error " Error: Should have a queue. This is likely a bug in React. Please file an issue"

我有 React 组件,它需要从组件内部使用自定义的 React Hook。

但是,只有在启用功能切换时才应调用此挂钩。我知道这是一种反模式,因为它违反了这里的钩子规则: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
      )

自定义挂钩:

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

现在错误轨迹是这样的:

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)

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

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
   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

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

const [someSubTopics] = useState([topics].flat());

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

const result = (!!featureToggles.Flag) && customHook(arg1, callbackMethod);

我在这里挠头,因为我不知道为什么会这样,任何想法或建议将不胜感激!!!!

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

const result = (!!featureToggles.Flag) && customHook(arg1, callbackMethod);

基本相同
let result = false
if (!!featureToggles.Flag){
  result = customHook(arg1, callbackMethod);
}

来自 React 文档:

Don’t call Hooks inside loops, conditions, or nested functions.

您可以做的是将 featureToggles 传递到您的自定义挂钩中,然后在其中进行检查。

确保你的条件检查在你的挂钩底部,在每个挂钩调用下面。

// pass featureToggles here
export default function myCustomizedHook(featureToggles, topics, messagesReceivedFn, subscriptionOptions = {}) {
  // your code here

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

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

  // make sure to do the check at the bottom, below every hook call
  if (!!featureToggles.Flag) {
    // handle this feature toggle
    return null
  }

  return { isConnected: isConnectorConnected, isReconnecting: isConnectorReconnecting, connectionStatus, subscribedTopics };