为什么 useEffect() 在从套接字接收数据后似乎重置了我的本地状态?
Why does the useEffect() seem to reset my local state after receiving data from a socket?
我有一个简单的反应组件,它通过 SocketIO 发送和接收消息。我的状态挂钩看起来像这样
const [newMessage, createMessage] = useState('');
const [messages, setMessage] = useState([]);
const [isConnected, setConnection] = useState(false);
想法是用户可以通过套接字编写和发送消息,然后接收回消息。用户在文本区域中输入一些文本。消息的内容被保存到 "newMessage" 本地状态变量 - 如有必要,下面的实现细节 - 然后用户通过套接字发送 newMessage。
const sendMessage = () => {
const currentMessage = {...newMessage}; //reduced for the sake of brevity
socket.emit('message', currentMessage);
createMessage('');
};
const composeMessage = (
<>
<div>
<textarea rows="2" cols="28" placeholder="Chat Message" onKeyDown={(e) => handleKeyDown(e)} onChange={(e) => createMessage(e.currentTarget.value)} value={newMessage} />
</div>
<div>
<button onClick={sendMessage}><i className="ra ra-horn-call" /></button>
</div>
</>
);
服务器端,消息收到,立即传回房间。我把监听消息响应的函数放到了useEffect hook中。我的想法是使用钩子代替 componentDidMount,因此我最初通过将一个空数组作为第二个参数传递给 useEffect 来实现它。当我这样做时,每当收到一条消息时,它似乎会清除我的 "messages" 状态,然后用新消息替换内容。从浏览器来看,似乎一次只能保持一条消息的状态——之前的消息不断被替换。我厌倦了在第二个参数数组中传递 "messages" 状态,消息现在似乎附加到状态,并且组件在收到时重新呈现 - 很酷,这就是我想要的?
我注意到我在每条后续消息上都遇到了性能问题,并最终发现套接字侦听器在每次渲染时都会重新应用。为了阻止这种情况,一位同事建议我向 state 添加一个布尔值,以防止在重新渲染时重新添加套接字。我更新了组件,结果与第一次没有将消息状态传递到第二个参数时的结果相同——它一直替换状态中的第一条消息,并且没有附加新消息。我只希望侦听器更新本地状态并在新消息通过套接字时重新呈现消息。我有点不知道如何用钩子来做到这一点。有人有什么想法吗?
我编写的 useEffect 挂钩的最终迭代如下。
useEffect(() => {
debugger
if (!isConnected) {
socket.on('message', (message) => {
const nextState = messages.slice();
nextState.push(message);
setMessage(nextState);
debugger
});
setConnection(true);
}
}, [messages, setConnection]);
根据您的代码,我认为 useEffect 将 运行 并进行多个订阅以在每次呈现时监听每条消息,只要消息和连接状态发生变化。这是因为只要状态发生变化,React 组件就会呈现。由于 websocket 连接和侦听器可以被视为副作用,因此您必须实现 return 回调以在组件卸载时处理它们。
假设你所有的 websocket 处理和聊天消息逻辑都在一个单一的反应组件中,你可能只需要像这样在挂载时创建 web 套接字连接和监听器,并提供一个 return 回调处理卸载,例如当您进行页面刷新时的场景,或者您的组件依赖于可能触发重新渲染的父状态:
useEffect(() => {
socket.connect();
socket.on('connect', () => {
console.log('socket connected:', socket.connected);
});
socket.on('message',...);
}
return () => {
console.log('websocket unmounting!!!!!');
socket.off();
socket.disconnect();
};
}, []);
我有一个简单的反应组件,它通过 SocketIO 发送和接收消息。我的状态挂钩看起来像这样
const [newMessage, createMessage] = useState('');
const [messages, setMessage] = useState([]);
const [isConnected, setConnection] = useState(false);
想法是用户可以通过套接字编写和发送消息,然后接收回消息。用户在文本区域中输入一些文本。消息的内容被保存到 "newMessage" 本地状态变量 - 如有必要,下面的实现细节 - 然后用户通过套接字发送 newMessage。
const sendMessage = () => {
const currentMessage = {...newMessage}; //reduced for the sake of brevity
socket.emit('message', currentMessage);
createMessage('');
};
const composeMessage = (
<>
<div>
<textarea rows="2" cols="28" placeholder="Chat Message" onKeyDown={(e) => handleKeyDown(e)} onChange={(e) => createMessage(e.currentTarget.value)} value={newMessage} />
</div>
<div>
<button onClick={sendMessage}><i className="ra ra-horn-call" /></button>
</div>
</>
);
服务器端,消息收到,立即传回房间。我把监听消息响应的函数放到了useEffect hook中。我的想法是使用钩子代替 componentDidMount,因此我最初通过将一个空数组作为第二个参数传递给 useEffect 来实现它。当我这样做时,每当收到一条消息时,它似乎会清除我的 "messages" 状态,然后用新消息替换内容。从浏览器来看,似乎一次只能保持一条消息的状态——之前的消息不断被替换。我厌倦了在第二个参数数组中传递 "messages" 状态,消息现在似乎附加到状态,并且组件在收到时重新呈现 - 很酷,这就是我想要的?
我注意到我在每条后续消息上都遇到了性能问题,并最终发现套接字侦听器在每次渲染时都会重新应用。为了阻止这种情况,一位同事建议我向 state 添加一个布尔值,以防止在重新渲染时重新添加套接字。我更新了组件,结果与第一次没有将消息状态传递到第二个参数时的结果相同——它一直替换状态中的第一条消息,并且没有附加新消息。我只希望侦听器更新本地状态并在新消息通过套接字时重新呈现消息。我有点不知道如何用钩子来做到这一点。有人有什么想法吗?
我编写的 useEffect 挂钩的最终迭代如下。
useEffect(() => {
debugger
if (!isConnected) {
socket.on('message', (message) => {
const nextState = messages.slice();
nextState.push(message);
setMessage(nextState);
debugger
});
setConnection(true);
}
}, [messages, setConnection]);
根据您的代码,我认为 useEffect 将 运行 并进行多个订阅以在每次呈现时监听每条消息,只要消息和连接状态发生变化。这是因为只要状态发生变化,React 组件就会呈现。由于 websocket 连接和侦听器可以被视为副作用,因此您必须实现 return 回调以在组件卸载时处理它们。
假设你所有的 websocket 处理和聊天消息逻辑都在一个单一的反应组件中,你可能只需要像这样在挂载时创建 web 套接字连接和监听器,并提供一个 return 回调处理卸载,例如当您进行页面刷新时的场景,或者您的组件依赖于可能触发重新渲染的父状态:
useEffect(() => {
socket.connect();
socket.on('connect', () => {
console.log('socket connected:', socket.connected);
});
socket.on('message',...);
}
return () => {
console.log('websocket unmounting!!!!!');
socket.off();
socket.disconnect();
};
}, []);