Javascript 为什么我的react元素渲染了很多次?
问题:我有以下反应组件。基本上,我正在react with socket.io中构建一个聊天应用程序Javascript 为什么我的react元素渲染了很多次?,javascript,node.js,reactjs,socket.io,Javascript,Node.js,Reactjs,Socket.io,问题:我有以下反应组件。基本上,我正在react with socket.io中构建一个聊天应用程序 import { io } from 'socket.io-client'; const socket = io(ServerEndURL); export default function Chat (props) { const uuid = props.uuid; const classes = useStyles(); const [open, setOpen]
import { io } from 'socket.io-client';
const socket = io(ServerEndURL);
export default function Chat (props) {
const uuid = props.uuid;
const classes = useStyles();
const [open, setOpen] = useState(false);
const [btnColor, setBtnColor] = useState('white');
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('isActive', uuid);
return () => {
console.log('removed');
socket.off('newMsg');
}
}, []);
socket.on('newMsg', msg => {
console.log('debug');
let msgs = msgArr.map(msg => msg);
msgs.push({
type : 'received',
msg,
});
setMsgArr(msgs);
if(!open) setUnreadMsgs(unreadMsgs + 1);
chatBoxRef.current.scrollTop = chatBoxRef.current.scrollHeight;
});
当服务器在套接字上触发newMsg事件时,我希望它用一个新的msg更新msgArr状态,但是我看到console.log('debug')语句的运行次数比预期的要多,并且事情并不像我预期的那样工作。我的意思是,它应该只对一次。但事实并非如此,它运行了多次,在某些时候,“次数”甚至达到了40次。作为新手,我不明白为什么会发生这种情况。因此,如果有人能解释一下这个问题,我将不胜感激。这个问题已经让我睡了好几天了。我需要帮助。有人吗?看起来你正试图直接改变状态,这是你永远不应该做的。请尝试以下方法:
import { io } from 'socket.io-client';
const socket = io(ServerEndURL);
export default function Chat (props) {
const uuid = props.uuid;
const classes = useStyles();
const [open, setOpen] = useState(false);
const [btnColor, setBtnColor] = useState('white');
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('isActive', uuid);
return () => {
console.log('removed');
socket.off('newMsg');
}
}, []);
socket.on('newMsg', msg => {
console.log('debug');
// Note: This is a no-op if you don't need to extract info from `msg`
let msgs = msgArr.map(msg => msg);
// Update state directly with the new message
setMsgArr([ ...msgs, { type: 'received', msg }]);
if(!open) setUnreadMsgs(unreadMsgs + 1);
chatBoxRef.current.scrollTop = chatBoxRef.current.scrollHeight;
});
对于问题的第二部分,您将告诉React您希望呈现到DOM中的内容,然后它将代表您负责实际更新DOM。因此,如果需要,它可能会多次重新呈现组件(通常是在状态更新时)。在您的例子中,您为更新状态的
newMsg
事件定义了一个事件处理程序,因此当触发此事件处理程序时,React将重新呈现您的组件(它执行console.log('debug')
语句)。我的假设是,每次React呈现您的组件时,您都会继续订阅newMsg事件。如果组件重新呈现40次,您将有40个对“newMsg”事件的订阅,它将运行您的事件回调40次。也就是说:
改为在“useEffect”钩子中定义“newMsg”事件侦听器
在现有的“useEffect”中,只要您仅将空数组([])证明为第二个参数:
useEffect(() => {
socket.on('newMsg', ...)
socket.emit('join', uuid);
socket.emit('isActive', uuid);
return () => {
console.log('removed');
socket.off('newMsg');
}
}, []);
或者,如果您的逻辑发生了变化,并且您“监控”了原始“useEffect”中的变化,您可以将其放在自己的“useEffect”中。(示例包括消息数组)
“useEffect”中的第二个参数只是告诉react,如果第二个参数中的某些内容发生更改,则重新运行效果。但是,您只想订阅一次,因此提供了一个空数组,这样它就永远不会看到更改并重新运行效果。谢谢,我没有改变状态。但是从你的回答中学到了一个很酷的技巧。但我不认为它对我的代码有太大的改变,问题仍然存在。我不知道该怎么办。我知道这是问题的一部分。我以前做过,也就是说,将它放在useEffect中,它解决了多次重新渲染,但又出现了一个错误。我正在向我的msgArr数组添加一个新的msg,但现在不行。每次触发newMsg事件时,似乎都会将msgArr的新实例传递给视图,而视图不记得以前的MSG。“我想我得多研究一下。”萨姆·戈梅纳已经回答了这一部分。使用他的示例更新您的数组:setMsgArr([…msgArr,{type:'received',msg}]);在您的事件回调->我已经编辑了我的回答否它不起作用。问题仍然存在。每次接收到“newMsg”事件时,数组中只有一个消息。所有其他MSG都已清除。
const [msgArr, setMsgArr] = useState([]);
useEffect(() => {
socket.on('newMsg', msg => {
setMsgArr([ ...msgArr, { type: 'received', msg }]);
//Alternative:
//setMsgArr(msgArr.concat({ type: 'received', msg }));
})
}, []);