如何防止每次导入自定义挂钩时调用 customHook 中的 useEffect?

How to prevent useEffect in customHook being called with every import of custom hook?

我正在使用 React js 和 socket.io 库编写聊天应用程序。

我从服务器订阅事件并发出一些事件的所有逻辑都写在自定义挂钩的 useEffect 中。

然后我 return 从这个自定义挂钩中获取我需要的所有数据,并在我需要的组件中重用它。但是,我意识到每次我将这个自定义钩子导入到外部组件时,都会调用用 useEffect 编写的逻辑。

如果我将所有逻辑都放在 useEffect 之外,它的调用次数甚至比导入自定义挂钩的次数还要多。

如果可能的话,我该如何预防?

如果不可行,您能建议什么解决方案?我不想为这个应用程序使用 redux,我想保留这个自定义挂钩组件中的所有内容,并在我需要的地方重用它的数据。

我无法分享工作示例,因为没有服务器部分它就无法工作,所以这里是一个简单的 codesandbox 示例。您可以在控制台中看到它被渲染了两次。

https://codesandbox.io/s/custom-hook-bfc5j?file=/src/useChat.js

如果您希望一次性设置一些副作用,但又想在多个地方使用生成的数据,一种方法是使用 context 功能。

// ws/context.jsx, or similar

const WsContext = React.createContext(defaultValue);

export const WsProvider = props => {
  const [value, setValue] = useState(someInitialValue);
  useEffect(() => {
    // do expensive things, call setValue with new results
  });
  return (
    <WsContext.Provider value={value}>
      {props.children}
    </WsContext.Provider>
  );
};

export const useCustomHook = () => {
  const value = useContext(WsContext);

  // perhaps do some other things specific to this hook usage

  return value;
};

您可以期望钩子在 React 渲染的元素树中 <WsProvider> 的任何后代组件中工作。

如果您在提供者组件的非后代中使用挂钩,则返回的值将是我们用来初始化上下文实例的defaultValue

它呈现两次,因为您在应用中调用了两次 useChat()(一次在 App.js,另一次在 Text.js) 你可以做的是在你的 App.js 中创建一个 useChat 组件的引用,并将它作为一个道具传递给 Text.js,例如:

App.js

import React from "react";
import useChat from "./useChat";
import Text from "./Text";
import "./styles.css";

export default function App() {
  const myUseChat = useChat();
  const { printMessage } = myUseChat;

  return (
    <div className="App">
      <button onClick={printMessage}>Print</button>
      <Text myUseChat={myUseChat} />
    </div>
  );
}

Text.js

import React from "react";
import useChat from "./useChat";
import "./styles.css";

export default function Text(props) {
  const { text } = props.myUseChat;

  return <div className="App">{text}</div>;
}