繁体   English   中英

为什么我的反应元素渲染多次?

[英]Why is my react element rendering many times?

问题:我有以下反应组件。 基本上,我正在构建一个与 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 state,但我看到console.log('debug')语句运行的次数比预期的要多,而且事情不起作用正如我所期望的那样。 我的意思是,它应该只对一次。 但它没有,它运行多次,在某些时候,“次”甚至达到 40。作为新的反应,我不明白为什么会发生这种情况。 因此,如果有人能对这个问题有所了解,我将不胜感激,这让我好几天都保持清醒。 我需要帮助。 任何人?

看起来您正在尝试直接突变 state ,这是您永远不应该做的。 试试这个:

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。 因此,如果需要,它可能会多次重新渲染您的组件(通常是在 state 已更新时)。 在您的情况下,您为更新 state 的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”中的变化,您可以简单地将其放在自己的位置。 (示例包括消息数组)

const [msgArr, setMsgArr] = useState([]);
useEffect(() => {
  socket.on('newMsg', msg => {
    setMsgArr([ ...msgArr, { type: 'received', msg }]);
    //Alternative:
    //setMsgArr(msgArr.concat({ type: 'received', msg }));
  })
}, []);

'useEffect' 中的第二个参数只是告诉 react 如果您的第二个参数中的某些内容发生更改,则重新运行效果。 但是,您只想订阅一次,因此提供了一个空数组,因此它永远不会看到更改并重新运行效果。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM