Javascript 为什么我的react元素渲染了很多次?

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]

问题:我有以下反应组件。基本上,我正在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] = 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 }));
  })
}, []);