繁体   English   中英

数据更改后如何使useEffect重新渲染?

[英]How to make useEffect rerender after the data is changed?

我希望 useEffect 获取和渲染rooms数据。 并在新房间添加到rooms时重新渲染数据

使用 useEffect 和空依赖项获取数据和显示数据的代码。

const [rooms, setRooms] = useState([]);

  const getRoomsData = async () => {
    const querySnapshot = await getDocs(collection(db, "rooms"));
    const data = querySnapshot.docs.map((doc) => ({
      id: doc.id,
      data: doc.data(),
    }));

    setRooms(data);
  };

  useEffect(() => {
    getRoomsData();
    console.log("rerendered");
  }, []);

使用此代码,在我将新房间添加到rooms 我需要手动刷新页面以查看呈现的新数据。 在将新“房间”添加到rooms后,我应该怎么做才能使其重新呈现?

添加房间的代码:

    const roomName = prompt("please enter name for chat room");

    if (roomName) {
      addDoc(collection(db, "rooms"), {
        name: roomName,
      });
    }
  };

我尝试向 useEffect 依赖项添加房间,这有我想要的结果(无需刷新),但这只是因为它无限渲染,这很糟糕。 正确的做法是什么?

您将获得无限的重新渲染,因为useEffect的函数正在更新效果依赖数组中的rooms状态。 这将始终导致无限重新渲染。

逐字回答您的问题:“如何仅在数据更改时才重新渲染?”

由于roomsdata设置为相同,您可以保持useEffect ,但随后创建另一个useEffect以仅在组件安装调用getRoomsData()

const [rooms, setRooms] = useState([]);

  const getRoomsData = async () => {
    const querySnapshot = await getDocs(collection(db, "rooms"));
    const data = querySnapshot.docs.map((doc) => ({
      id: doc.id,
      data: doc.data(),
    }));

    setRooms(data);
  };

  useEffect(() = > {
    getRoomsData();
  }, [])

  useEffect(() => {
    console.log("rerendered");
  }, [rooms]);

我认为解决您的问题的真正关键是知道何时调用getRoomsData() ,因为根据这一点,您将更改useEffect依赖项数组以适应该需要。

使用 useEffect 获取数据时,它期望进行同步调用。 从 Firestore 请求数据将是异步的,因此为了解决这个问题,我们可以添加一个函数来包装名为 getRoomsData 的 firestore 调用,如下所示:

 const [rooms, setRooms] = useState([]); useEffect(() = > { const getRoomsData = async () => { const querySnapshot = await getDocs(collection(db, "rooms")); const data = querySnapshot.docs.map((doc) => ({ id: doc.id, data: doc.data(), })); setRooms(data); }; getRoomsData(); }, [])

我可以想到两种解决这个问题的方法,而不必使用useEffect ,第一种是一种解决方法,您可以在本地更新rooms状态而不必从服务器获取它,即,您基本上是在构建对象并将其附加到现有的rooms数组,只有当您知道对象的结构时才有可能,查看您的getRoomsData函数,您似乎正在返回一个数据数组,其中每个项目都具有以下对象结构。

{
 id: <DOCUMENT_ID>,
 data: {
  name: <ROOM_NAME>
 }
}

因此,当您添加房间时,您可以执行以下操作:

   const roomName = prompt("please enter name for chat room");

    if (roomName) {
      addDoc(collection(db, "rooms"), {
        name: roomName,
      });
      let newRoom = {
        id: Math.random(), //random id
        data: {
          name: roomName
        }
      }
      setRooms([...rooms,newRoom])
  };

通过这种方式,您还可以减少网络调用的数量,这是一个性能奖励,但唯一的缺点是您必须确定对象结构,以便相应地绘制结果。
如果您不确定对象结构,您可以做的是在将新房间添加到集合后调用getRoomsData ,如下所示:

   const roomName = prompt("please enter name for chat room");

    if (roomName) {
      addDoc(collection(db, "rooms"), {
        name: roomName,
      }).then(res=>{
          getRoomsData();
      })
  };

这将确保获取最新数据(包括最近添加的房间)并使用所有结果呈现您的组件。 在您遵循的任一方法中,当组件使用空依赖项数组挂载时,您只能使用一次useEffect

 useEffect(() => {
    getRoomsData();
    //console.log("rerendered");
  }, []);

通过这样做,您首先基本上显示用户第一次访问此页面时/第一次安装组件时的数据。 在后续添加时,您可以在将新添加的room添加到数据库后将其作为对象附加到rooms数组,或者将其添加到数据库并再次获取所有结果,以便在屏幕上呈现最新添加的rooms

暂无
暂无

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

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