簡體   English   中英

如何防止在 React 中使用 useEffect() 無限重新渲染?

[英]How to prevent infinite re-rendering with useEffect() in React?

我是開發新手。我使用 socket.io 創建了一個聊天應用程序。我只有在套接字更改時才會更改 useEffect,但我在 useEffect() 的主體中有 setMessage 意味着更新消息,我將套接字作為依賴項。但是這個不起作用,它無限地重新運行。

import React from 'react';
import ScrollToBottom from 'react-scroll-to-bottom';
import { useState,useEffect } from 'react';

function Chat({socket,username,room}) {
    const [currentMessage, setCurrentMessage] = useState("");
    const [messageList, setMessageList] = useState([]);
    const sendMessage = async () => {
        if (currentMessage !== "") {
            const messageData = {
                room: room,
                author: username,
                message: currentMessage,
                time: new Date(Date.now()).getHours()
                    + ':' +
                    new Date(Date.now()).getMinutes()
            };
            await socket.emit("send_message", messageData);
            sendMessage((list)=>[...list,messageData]);
            
        }
    };
    useEffect(() => {
        socket.on("receive_message", (data) => {
          setMessageList((list) => [...list, data]);
          setCurrentMessage("");
          
        });
      }, [socket]);
    return (
        <div className='chat-window'>
            <div className='chat-header'>
                <p>Live Chat</p>
            </div>
            <div className='chat-body'>
                <ScrollToBottom className='message-container'>
                {messageList.map((messageContent)=>{
                    return (
                    <div className='message' id={username===messageContent.author?"you":"other"}>
                       <h1>{messageContent.message}</h1>
                    </div>
                    );
                
                    })}
                </ScrollToBottom>

            </div>
            <div className='chat-footer'>
                <input type="text" value={currentMessage}placeholder='Hey..' onChange={(event) => {
                    setCurrentMessage(event.target.value);
                }} 
                onKeyPress={(event)=>{
                    event.key==='Enter'&&sendMessage();
                }}/>
                <button onClick={sendMessage}>&#9658;</button>
            </div>
        </div>
    );
}
export default Chat;

如何停止渲染 useEffect function?

sendMessage Function 發生遞歸,導致死循環。

刪除以下行可能會解決問題

sendMessage((list)=>[...list,messageData]);

另外,不要忘記從依賴項數組中刪除套接字

實際上我最近遇到了這個問題,但是您可以嘗試一些選擇:

  1. 在初始化socket的文件中添加監聽器

不要將套接字傳遞給子組件,而應該只將數據傳遞給子組件

示例index.js

const [messageList, setMessageList] = useState([]);

// Do not use any deps here
useEffect(() => {
   let socket = init() // or something

   socket.on("receive_message", (data) => {
      setMessageList((list) => [...list, data]);
      setCurrentMessage("");
   });
}, [])

return (
    <ChildComponent messageList={messageList} />
)

使用這個解決方案, useEffect應該只渲染一次,但是如果你想傳遞套接字,你可以使用useRef而不是useEffect ,因為它不會重新渲染並且像普通變量一樣工作。

  1. 使用自定義 Window 調度事件掛鈎

我知道你想在這里擁有干凈的代碼,所以也許替代方案是使用 EventTarget.dispatchEvent() 瀏覽器 API,這種方法是我通常使用的方法。

有了這個,您只需要創建自定義掛鈎來發出/收聽數據,這是我的:

const [data, setData] = useState<T | null>(null);

useEffect(() => {
    const  handler = ((event: CustomEvent<CustomEventDetail>) => {
        if (event.detail.emitId === id) return;
        setData(event.detail.data);

    }) as  EventListener;

    window.addEventListener(eventName, handler);

    return () => {
        window.removeEventListener(eventName, handler);
    };
}, [eventName, id]);
  
const  emit = (data: T) => {
    const  customEvent = new  CustomEvent<CustomEventDetail>(eventName, {
        detail: {
            emitId:  id,
            data:  data,
        },
    });

    setData(data);
    window.dispatchEvent(customEvent);
};
  1. useEffect中創建Validator

也許您想嘗試的最后一件事就是使用驗證器

useEffect(() => {
    socket.on("receive_message", (data) => {
        if (currentMessage !== data.message) {
            setMessageList((list) => [...list, data]);
            setCurrentMessage("");
        }
    });
}, [socket]);

但是,我不知道它是否適用於 don`t


另外對於筆記,不要忘記在useEffect上使用 return ,所以它里面的 function 不會因為組件重新渲染而被多次調用,例如

const handler = () =>  {}
socket.on("receive_message", handler);

return () => {
    socket.off("receive_message", handler);
}

希望我的評論能給你一個解決方案,讓我知道以上是否適合你!

為什么要將 receive_message 套接字偵聽器傳遞給 useEffect? 你試過不使用 useEffect

socket.on("receive_message", (data) => {
      setMessageList((list) => [...list, data]);
      setCurrentMessage("");
});

我認為您不必將套接字作為依賴項傳遞。

測試這個片段,讓我知道它是否有效。

useEffect(() => {
  socket.on("receive_message", (data) => {
     setMessageList((list) => [...list, data]);
     setCurrentMessage("");        
  });
  
  return () => {
     socket.off('receive_message');
  }
}, []);

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM