如何防止 React 重新渲染函数组件中的每个函数

How to prevent React from re-rendering every function in the function component

我让两个用户加入同一个 Socket.io 房间,让他们有一条私人消息,该房间基于发送者和接收者 ID 创建的房间。每次我创建消息时,由于状态更改导致 Socket.io 发出事件重新呈现,客户端重新加入房间,并且 roomId 被控制台记录。因此,当消息加入同一个房间时,消息不会在多个聊天实例中传输。

const ChatApp = () => {
  const [messages, setMessages] = React.useState([]);
  const userId = useSelector(state => state.profile.profile._id);
  const senderFullName = useSelector(state => state.auth.user.fullname);
  const authId = useSelector(state => state.auth.user._id);
  const roomId = [authId, userId].sort().join('-');
  const ioClient = io.connect("http://localhost:5000"); 

    ioClient.emit('join', {roomid: roomId});
    ioClient.emit('connected', {roomid: roomId} );
    ioClient.on('message', (msg) => {
      const messageObject = {
        username: msg.from,
        message : msg.body,
        timestamp: msg.timestamp
      };
      addMessage(messageObject);
    });

  const addMessage = (message) => {
   const messagess = [...messages];
   messagess.push(message); 
   setMessages(messagess);
   console.log(messagess);   
   }

   const sendHandler = (message) => {
    var res = moment().format('MM/DD/YYYY h:mm a').toString();

    // Emit the message to the server
    ioClient.emit('server:message', {from: senderFullName, body: message, timestamp: res });
   }
   
     return (

<div className="landing">
<Container>
 <Row className="mt-5">
   <Col md={{ span: 8, offset: 2 }}>

     <Card style={{ height: "36rem" }} border="dark">
       <Messages msgs={messages} />
       <Card.Footer>
       <ChatInput onSend={sendHandler}>
         </ChatInput>
       </Card.Footer>
     </Card>
   </Col>
 </Row>
</Container>
</div>
   )
 };

Api 服务器中使用的代码

    io.sockets.on("connection",function(socket){
        // Everytime a client logs in, display a connected message
        console.log("Server-Client Connected!");

        socket.on('join', function(data) {

           socket.join(data.roomid, () => {
               //this room id is being console logged every time i send a message
               console.log(data.roomid);
           }); 

        });    

         socket.on('connected', function(data) {    
            //load all messages
            console.log('message received');
            (async () => {
                try {
                    console.log('searching for Schema');
            const conversation = await Conversation.find({roomId: data.roomid}).populate('messages').lean().exec();
            const mess = conversation.map(); 
            console.log(mess);   
            console.log('Schema found');    
            }
               catch (error) {
                   console.log('Schema being created');
                Conversation.create({roomId: data.roomid});
               }
        })();

    });

        socket.on('server:message', data => {
 
            socket.emit('message', {from: data.from, body: data.body, timestamp: data.timestamp});
            console.log(data.body);
            Message.create({from: data.from, body: data.body});

                
        })     
    });

您可以将套接字状态向上移动到父组件,或者将事件初始化移动到一个在您的应用程序中共享一次的自定义挂钩中,或者移动到此处的 useEffect 挂钩中,因此它只 [=初始渲染时 31=]s。

可能最简单的是后一种选择,你可以把那些你只想写一次的代码行像这样包装起来:

useEffect(() => {
  ioClient.emit('join', {roomid: roomId});
    ioClient.emit('connected', {roomid: roomId} );
    ioClient.on('message', (msg) => {
      const messageObject = {
        username: msg.from,
        message : msg.body,
        timestamp: msg.timestamp
      };
      addMessage(messageObject);
    });
}, []);

末尾的空数组表示这只会 运行 在初始渲染时,不会再次渲染,因为它没有依赖关系。


更新

您可能有也可能没有关闭或计时问题,但为了安全起见,请将行“addMessage(messageObject);`”替换为:

setMessages((previousMessages) => [messageObject, ...previousMessages]);

这将做与 addMessage 函数相同的事情,但是通过将回调函数传递给 setMessages,您可以避免从外部使用状态对象。