如何让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
的状态。这将始终导致无限重新渲染。
逐字回答您的问题:
“如何让它仅在数据更改时重新呈现?”
由于 rooms
和 data
设置相同,您可以保持 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");
}, []);
通过这样做,当用户第一次访问此页面时,您首先基本上会显示数据 time/when 组件是第一次安装。后续添加时,可以将新添加的room
作为对象添加到数据库后追加到rooms
数组中,也可以将其添加到数据库中并再次获取所有结果,以便最新的添加呈现在屏幕上。
我希望 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
的状态。这将始终导致无限重新渲染。
逐字回答您的问题: “如何让它仅在数据更改时重新呈现?”
由于 rooms
和 data
设置相同,您可以保持 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");
}, []);
通过这样做,当用户第一次访问此页面时,您首先基本上会显示数据 time/when 组件是第一次安装。后续添加时,可以将新添加的room
作为对象添加到数据库后追加到rooms
数组中,也可以将其添加到数据库中并再次获取所有结果,以便最新的添加呈现在屏幕上。