[英]React useState hook not consistently updating
我开始将 websockets 集成到现有的 React/Django 应用程序中,并遵循此示例(此处随附 repo)。 在该仓库中,websocket 接口位于websockets.js
中,并在containers/Chat.js
中实现。
我可以让该代码按原样正常工作。
然后我开始重写我的实现以使用 Hooks,并碰了壁。 数据正确流过socket,正确到达每个客户端的handler,handler内可以读取到正确的state。 在该处理程序中,我调用我的 useState function 以使用传入数据更新 state。
最初我在addMessage()
中的单个useState
function 不一致触发(10 次中有 1 次?)。 我将我的一个useState
挂钩分成两个(一个用于当前消息,一个用于所有消息)。 现在在addMessage()
从服务器接收数据后,我的setAllMessages
钩子只会更新我输入消息的客户端 - 没有其他客户端。 所有客户端都接收/可以正确记录数据,他们只是不运行setAllMessages
function。
如果我推送到 function 之外的空数组,它会按预期工作。 所以这似乎是 function 更新周期中的一个问题,但我一直无法追踪它。
这是我的websocket.js
版本:
class WebSocketService {
static instance = null;
static getInstance() {
if (!WebSocketService.instance) {
WebSocketService.instance = new WebSocketService();
}
return WebSocketService.instance;
}
constructor() {
this.socketRef = null;
this.callbacks = {};
}
disconnect() {
this.socketRef.close();
}
connect(chatUrl) {
const path = `${URLS.SOCKET.BASE}${URLS.SOCKET.TEST}`;
this.socketRef = new WebSocket(path);
this.socketRef.onopen = () => {
console.log('WebSocket open');
};
this.socketRef.onmessage = e => {
this.socketNewMessage(e.data);
};
this.socketRef.onerror = e => {
console.log(e.message);
};
this.socketRef.onclose = () => {
this.connect();
};
}
socketNewMessage(data) {
const parsedData = JSON.parse(data);
const { command } = parsedData;
if (Object.keys(this.callbacks).length === 0) {
return;
}
Object.keys(SOCKET_COMMANDS).forEach(clientCommand => {
if (command === SOCKET_COMMANDS[clientCommand]) {
this.callbacks[command](parsedData.presentation);
}
});
}
backend_receive_data_then_post_new(message) {
this.sendMessage({
command_for_backend: 'backend_receive_data_then_post_new',
message: message.content,
from: message.from,
});
}
sendMessage(data) {
try {
this.socketRef.send(JSON.stringify({ ...data }));
} catch (err) {
console.log(err.message);
}
}
addCallbacks(allCallbacks) {
Object.keys(SOCKET_COMMANDS).forEach(command => {
this.callbacks[SOCKET_COMMANDS[command]] = allCallbacks;
});
}
state() {
return this.socketRef.readyState;
}
}
const WebSocketInstance = WebSocketService.getInstance();
export default WebSocketInstance;
这是我的Chat.js
版本
export function Chat() {
const [allMessages, setAllMessages] = useState([]);
const [currMessage, setCurrMessage] = useState('');
function waitForSocketConnection(callback) {
setTimeout(() => {
if (WebSocketInstance.state() === 1) {
callback();
} else {
waitForSocketConnection(callback);
}
}, 100);
}
waitForSocketConnection(() => {
const allCallbacks = [addMessage];
allCallbacks.forEach(callback => {
WebSocketInstance.addCallbacks(callback);
});
});
/*
* This is the problem area
* `incoming` shows the correct data, and I have access to all state
* But `setAllMessages` only updates on the client I type the message into
*/
const addMessage = (incoming) => {
setAllMessages([incoming]);
};
// update with value from input
const messageChangeHandler = e => {
setCurrMessage(e.target.value);
};
// Send data to socket interface, then to server
const sendMessageHandler = e => {
e.preventDefault();
const messageObject = {
from: 'user',
content: currMessage,
};
setCurrMessage('');
WebSocketInstance.backend_receive_data_then_post_new(messageObject);
};
return (
<div>
// rendering stuff here
</div>
);
}
无需使用钩子将所有内容重写为功能组件。
您应该在功能上分解它- 用于初始化的主(父级、类/FC)并向负责呈现列表和输入(新消息)的 2 个功能性子级/组件提供 [数据和] 方法(作为道具)。
如果您仍然需要它... useEffect
是一个关键...因为所有代码都在功能组件中的每个渲染上运行...包括 function 定义、重新定义、新参考、回调数组中的重复等。
您可以尝试将所有曾经定义的函数移动到useEffect
useEffect(() => {
const waitForSocketConnection = (callback) => {
...
}
const addMessage = (incoming) => {
setAllMessages([incoming]);
};
waitForSocketConnection(() => {
...
}
}, [] ); // <<< RUN ONCE
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.