javascript - 为什么状态没有正确更新?
问题描述
我正在构建一个聊天应用程序。现在我遇到的问题是,当我单击一个按钮时,handleMsgSend 按钮会运行,并向另一个客户端发送一个“newMsg”事件。收到此事件后,我希望 msgArr 状态会使用新收到的 msg 进行更新,但没有。里面全是新的味精,没有别的了。为什么会这样?我觉得我在学习反应时跳过了技术内容。
export default function Chat (props) {
const uuid = props.uuid;
const classes = useStyles();
const [open, setOpen] = useState(false);
const [activeStatus, setActiveStatus] = useState('Offline');
const [unreadMsgs, setUnreadMsgs] = useState(0);
const [msgArr, setMsgArr] = useState([]);
const chatBtnRef = useRef();
const chatBoxRef = useRef();
const msgInputRef = useRef();
useEffect(() => {
socket.emit('join', uuid);
socket.emit('isOnline', uuid);
socket.on('newMsg', msg => {
setMsgArr([ ...msgArr, { type: 'received', msg }]);
console.log(msgArr);
if(!open) setUnreadMsgs(unreadMsgs + 1);
chatBoxRef.current.scrollTop = chatBoxRef.current.scrollHeight;
});
socket.on('isOnline', () => {
setActiveStatus('Online');
socket.emit('isOnline');
});
return () => {
console.log('removed');
socket.off('newMsg');
socket.off('Online');
}
}, []);
const handleMsgSend = e => {
e.preventDefault();
let msg = msgInputRef.current.value;
setMsgArr([ ...msgArr, { type: 'sent', msg }]);
e.currentTarget.reset();
socket.emit('newMsg', {uuid, msg});
chatBoxRef.current.scrollTop = chatBoxRef.current.scrollHeight;
}
const toggleChat = () => {
setUnreadMsgs(0);
if(open) setOpen(false);
else setOpen(true);
}
我如何呈现 msgArr:
<div ref={ chatBoxRef } className={ classes.chatBox }>
{
msgArr.map((msgObj, index) => {
return (
<div key={index} className={`msg-container ${(msgObj.type == 'sent')? 'myMsg' : 'hisMsg'}`}>
<span className='msg'>
{ msgObj.msg }
</span>
</div>
)
})
}
</div>
解决方案
您的套接字侦听器附加在useEffect
仅在初始渲染时执行的 a 中,因此您在其中看到的 msgArr 的值将始终引用由于关闭而在 state 中定义的初始值。
现在,由于 msgArr 是从套接字侦听器的闭包中使用的,因此即使您收到新消息,您也将始终引用初始状态而不是更新状态。
要解决这个问题,您必须使用callback
方法来更新状态
socket.on('newMsg', msg => {
setMsgArr(prevMsgArr => [ ...prevMsgArr, { type: 'received', msg }]);
if(!open) setUnreadMsgs(unreadMsgs + 1);
chatBoxRef.current.scrollTop = chatBoxRef.current.scrollHeight;
});
一旦你这样做了,你将能够在下一个渲染周期中看到状态的更新值,从而能够基于数组渲染更新的内容
PS 请注意,console.log(msgArr);
紧随其后setMsgArr
不会给你更新的值,因为状态更新是异步的并且受关闭影响。请参阅此帖子以获取有关此内容的更多信息。
推荐阅读
- python - 如何从我在 Python 中创建的文件中导入模块?
- javascript - 将 Axios 添加到 React 图表并获取 JSON 数据
- matlab - fmi2Instantiate:未定义的函数或变量“fmuhelper”
- awk - 如何从文件的一部分写入字符串和比列?
- firebase - Firebase 云函数多次执行
- sql - 如何获得不同的记录?
- node.js - 如何从猫鼬内部调用 req.flash() ?
- javascript - Vue 单元测试:使用错误导入组件的函数
- python - 将 2D 数组元素插入 3D 数组 Python
- javascript - JavaScript:将没有时区的时间戳转换为毫秒