如何在useEffect
内部建立连接,同时使用自定义钩子装饰该连接?自定义钩子不允许在useEffect
内部运行,并且在渲染期间不允许ref.current
。什么是用React友好的接口 Package 连接的正确方法?
我相信在React中建立WebSocket
连接的正确方法是使用useEffect
钩子,沿着
const connection = useRef(null);
useEffect(() => {
const ws = new WebSocket(socketUrl);
connection.current = ws;
return () => {
ws.close();
connection.current = null;
}
}, [socketUrl]);
以类似的方式,我设想创建一个RTCDataConnection
,它遵循与.send('...')
和open
,close
,error
和message
事件相同的连接接口。
我正在寻找一种方法来为这些连接提供一个类似React的接口。我在考虑像下面这样的自定义useConnection
钩子,它接受一个JavaScript连接对象,并返回两个React状态变量lastMessage
和readyState
,以及一个方法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
。它会向我抛出“无效的hook call” - 我也不能在
useEffect
之外引用connection.current
,因为这违反了the rules ofuseRef
:渲染期间不允许访问.current
属性。
所以我的问题是,我如何将连接和挂钩结合在一起?或者我应该使用完全不同的方法?
3条答案
按热度按时间osh3o9ms1#
如果你想这样做,例如。叫钩
从
useEffect
内部,因为它是唯一可以访问something
的地方,所以这是不可能的。钩子应该在函数组件的主体中被调用,而不是有条件的,也不是在一个循环中,你可以把它们看作是一个导入。
解决方案:
创建一个状态
connection
并将其值作为useConnection
函数参数**注意:**您可能需要在钩子中做一些额外的工作来处理收到的
null
连接,因为这将在组件首次挂载时调用钩子时发生现在,从
useEffect
开始,而不是调用钩子,你只需要用你想要调用你的自定义钩子的值(即something
)来更新connection
状态。当状态被更新时,组件会重新呈现,如果钩子函数接收到一个新的值作为参数,它会被调用,因此当
connection
得到一个新的值时,它会返回一个新的{lastMessage, readyState, sendMessage}
值,在这个新的呈现中,你可以访问它。因此,如果您想在更新
connection
状态后直接在useEffect
中使用它们,那么这是不可能,但是,您仍然可以创建另一个useEffect
,每次更新connection
时都会触发它,所以在组件重新-通过在第一个useEffect
中更新connection
来呈现,因为它是一个新的呈现,所以您可以访问第二个useEffect
。当执行
connection.send(msgJSON)
时,该函数应该与预期的connection
对象一起工作,因为它是一个接收预期的connection
的新示例。在这里,我添加了connection?.id
作为依赖项,而不是connection
,因为我们想避免包含对象,这样的键应该存在于connection
对象中。注意
useEffect
钩子在每次更新其依赖数组中的值时都会触发,而且在组件首次挂载时也会触发。whhtz7ly2#
您可能希望在组件的顶层调用钩子,然后在
useEffect()
中使用它返回的部分你的方法看起来太棒了!
bq8i3lrv3#
最后,我重新设计了
useConnection
,使其采用工厂函数,而不是直接采用连接对象。这不仅绕过了useRef
,还使调用代码更加清晰(并且与the React devs views更加一致)。下面是如何使用useConnection
钩子:这就是全部。(如果
socketUrl
是可变的,则可以将其 Package 在useMemo
中。)useConnection
现在看起来像这样:由于公认的答案与原来的问题较为接近,我将保持原样。