如何在useEffect内设置连接,同时使用自定义钩子装饰该连接?在渲染期间,不允许在useEffect内运行自定义挂钩,也不允许在ref.current内运行.用react 友好的界面包装连接的正确方式是什么?

我认为在Reaction中建立WebSocket连接的正确方法是使用useEffect挂钩,如下所示

const connection = useRef(null);

useEffect(() => {
  const ws = new WebSocket(socketUrl);
  connection.current = ws;

  return () => {
    ws.close();
    connection.current = null;
  }
}, [socketUrl]);

以类似的方式,我设想创建一个RTCDataConnection,它遵循与.send('...')opencloseerrormessage事件相同的连接接口.

我正在寻找一种方法,为这些连接提供一个类似react 的接口.我的 idea 类似于下面的定制useConnection挂钩,它接受一个JavaScript Connection对象,并提供两个react 状态变量lastMessagereadyState,以及一个方法sendMessage:

export function useConnection(connection) {
  const [lastMessage, setLastMessage] = useState(null);
  const [readyState, setReadyState] = useState(null);

  const onReadyStateChange = useCallback(
    (e) => {
      console.info(e);
      setReadyState(connection.readyState);
    },
    [setReadyState]
  );

  const onMessage = useCallback(
    ({ data }) => {
      const message = JSON.parse(data);
      setLastMessage(message);
    },
    [setLastMessage]
  );

  function sendMessage(message) {
    const msgJSON = JSON.stringify(message);
    connection.send(msgJSON);
  }

  useEffect(() => {
    connection.addEventListener('open', onReadyStateChange);
    connection.addEventListener('close', onReadyStateChange);
    connection.addEventListener('error', onReadyStateChange);
    connection.addEventListener('message', onMessage);

    return () => {
      connection.removeEventListener('open', onReadyStateChange);
      connection.removeEventListener('close', onReadyStateChange);
      connection.removeEventListener('error', onReadyStateChange);
      connection.removeEventListener('message', onMessage);
    };
  }, [connection]);

  return { lastMessage, readyState, sendMessage };
}

然而,我不知道如何正确地将挂钩应用于连接.

  • 到了the rules of hooks,我打不通useEffect内线的useConnection.它向我抛出"无效的钩子调用".
  • 我也不能引用useEffect之外的connection.current,因为这违反了the rules of useRef:在呈现期间不允许访问.current属性.

所以我的问题是,我如何将连接和挂钩结合在一起?或者我应该使用一种完全不同的方法?

推荐答案

如果要执行此操作,即调用钩子

const {lastMessage, readyState, sendMessage} = useConnection(something);

useEffect内,因为它是您可以访问something的唯一位置,因此这是不可能的.
钩子应该在函数组件的主体中调用,而不是有条件的,也不应该在循环内调用,您可以将它们视为导入.

解决方案:

创建一个状态connection,并将其值用作useConnection函数参数

const [connection, setConnection] = useState(null);
const { lastMessage, readyState, sendMessage } = useConnection(connection);

Note:您可能需要在钩子中做一些额外的工作来处理收到的null个连接,因为这将在组件第一次挂载中调用钩子时发生

现在从useEffect开始,而不是调用钩子,您只需使用您希望使用something调用自定义钩子的值来更新connection状态.
当状态更新时,组件重新呈现,并且您的钩子函数将被调用(如果它接收到一个新值作为参数,因此当connection获得一个新值时,因此它返回一个新的{lastMessage, readyState, sendMessage}值,您可以在这个新的呈现中访问它

因此,如果您想要在更新connection状态后直接在useEffect中使用它们,那么这是不可能的,但是,您仍然可以在每次更新connection时创建另一个useEffect,所以在组件通过现在更新第一个useEffect中的connection重新渲染后,由于它是新渲染,您将可以访问第二个useEffect中挂钩返回的新值.

useEffect(() => {
 // continue your logic here
 if(connection){
   sendMessage("new msg")
 }
},[connection?.id]) 

该函数在执行connection.send(msgJSON)时应该使用预期的connection对象,因为它是一个接收预期的connection的新实例 在这里,我添加了connection?.id作为依赖项,而不是connection,因为我们希望避免包含对象,这样的键应该存在于connection对象中

Note每次更新其依赖数组中的值时,也会在组件第一次挂载时触发useEffect钩子.

Reactjs相关问答推荐

迁移到React Router Dom v6后,我是否错误地使用了Link组件?

react 路由导航不工作,但手动路由工作

Formik验证不适用于物料UI自动完成

react -在选项中 Select 我想要显示和图像.但当我 Select 该选项时,我希望控件仅显示该选项的文本部分

为什么我的react虚拟化列表不显示滚动条,除非我确保高度低于rowCount*rowHeight?

Redux Store未触发React组件中的重新呈现

使用REACT-RUTER-DOM链接仅更新一个搜索参数

在React中映射对象数组时,如何正确呈现JSX.Element或React.ReactNode类型?

如何清除过滤器搜索的状态

React Wordpress Apache 在索引之外重新加载 -> 未找到页面

使用jest如何覆盖对象的模拟?我正在使用`jest.mock()`来模拟对象,并希望 for each 测试用例覆盖模拟.

在 React JS 中编辑表格数据

Next.js i18n 路由不适用于动态路由

docker:来 automorphic 护进程的错误响应:未指定命令.的原因是什么?

有没有办法根据 Rechart 中的 activeDot 更改值?

list 组件未按预期运行

如何在props 更改时运行自定义挂钩?

如何跨微前端 React 应用管理状态管理?

在渲染不显示子元素之后,再次渲染时,子元素状态数据完全消失了.为什么会这样以及如何防止它发生?

我可以在类组件中使用 useState 挂钩吗?