我需要连接到WebSockets服务器并记录其消息。使用React类组件,我可以将此逻辑放入componentDidMount
生命周期挂钩中并愉快地继续前进,但是我不确定如何使用挂钩正确地实现它。
这是我的第一次尝试。
import React, {useEffect} from 'react';
export default function AppWs() {
useEffect(() => {
let ws = new WebSocket('wss://ws.kraken.com/');
ws.onopen = () => console.log('ws opened');
ws.onclose = () => console.log('ws closed');
ws.onmessage = e => {
const message = JSON.parse(e.data);
console.log('e', message);
};
return () => {
ws.close();
}
}, []);
return (
<div>hooks + ws</div>
)
}
我向useEffect
添加了连接和日志逻辑,提供了具有依赖性的空数组,并且一切正常。直到我需要添加pause
状态来暂停日志记录。
export default function AppWs() {
const [isPaused, setPause] = useState(false);
useEffect(() => {
let ws = new WebSocket('wss://ws.kraken.com/');
ws.onopen = () => console.log('ws opened');
ws.onclose = () => console.log('ws closed');
ws.onmessage = e => {
if (isPaused) return;
const message = JSON.parse(e.data);
console.log('e', message);
};
return () => {
ws.close();
}
}, []);
return (
<div>
<button onClick={() => setPause(!isPaused)}>{isPaused ? 'Resume' : 'Pause'}</button>
</div>
)
}
ESLint开始对我大吼大叫,我应该将isPaused
状态添加为对useEffect
的依赖。
好,好了。
但是,每次单击按钮后,我都发现重新连接到WS服务器。显然这不是我想要的。
我的下一个迭代是使用两个useEffect
:一个用于连接,另一个用于消息处理。
export default function AppWs() {
const [isPaused, setPause] = useState(false);
const [ws, setWs] = useState(null);
useEffect(() => {
const wsClient = new WebSocket('wss://ws.kraken.com/');
wsClient.onopen = () => {
console.log('ws opened');
setWs(wsClient);
};
wsClient.onclose = () => console.log('ws closed');
return () => {
wsClient.close();
}
}, []);
useEffect(() => {
if (!ws) return;
ws.onmessage = e => {
if (isPaused) return;
const message = JSON.parse(e.data);
console.log('e', message);
};
}, [isPaused, ws]);
return (
<div>
<button onClick={() => setPause(!isPaused)}>{isPaused ? 'Resume' : 'Pause'}</button>
</div>
)
}
这可以按预期工作,但是我有一种想念的感觉,可以通过一个useEffect
来轻松解决此任务。
请帮助重构代码,以使我确信我正在以适当的方式使用React挂钩。谢谢!
答案 0 :(得分:29)
由于您只设置了Web套接字一次,我认为更好的方法是使用ref而不是状态:
export default function AppWs() {
const [isPaused, setPause] = useState(false);
const ws = useRef(null);
useEffect(() => {
ws.current = new WebSocket("wss://ws.kraken.com/");
ws.current.onopen = () => console.log("ws opened");
ws.current.onclose = () => console.log("ws closed");
return () => {
ws.current.close();
};
}, []);
useEffect(() => {
if (!ws.current) return;
ws.current.onmessage = e => {
if (isPaused) return;
const message = JSON.parse(e.data);
console.log("e", message);
};
}, [isPaused]);
return (
<div>
<button onClick={() => setPause(!isPaused)}>
{isPaused ? "Resume" : "Pause"}
</button>
</div>
);
}
useEffect
的顺序很重要。