在 React Native 中使用 useEffect 的正确方法
Right way to use useEffect in react native
这是我的示例 React 组件:
const OwnerView = () => {
const [monthlyCharge, setMonthlyCharge] = useState(0)
useEffect(() => {
getPerMonthCharges(ownerPhoneNumber, vehicles.length)
}, [])
async function getPerMonthCharges(ownerPhoneNumber, noOfCars) {
console.log(`inside getPerMonthCharges`);
try {
const serviceProviderChargesDoc = await firestore().collection(`${serviceProviderId}_charges`).doc(`${ownerPhoneNumber}`).get()
if (serviceProviderChargesDoc?.data()?.chargesPerMonth > 0) {
setMonthlyCharge(serviceProviderChargesDoc?.data()?.chargesPerMonth)
return
}
} catch (error) {
console.log(`Error while fetching monthly charge ${error}`);
}
setMonthlyCharge(noOfCars * perMonthGeneralCharge)
console.log(`done with getPerMonthCharges`);
}
}
即使在 getPerMonthCharges() 完成执行之前,OwnerView 也有可能被卸载。因此,如果 OwnerView 被卸载,我会收到一条警告,指出正在对未安装的组件进行状态更新,这是一个非操作。有人可以强调一下您的观察结果和编写这段代码的正确方法吗?
有很多方法可以解决这个问题
- 您可以检查组件是否仍处于挂载状态,我同意这种方法有点难看,但相当标准(我只使用
react-use
中的 useAsync
之类的方法,这实际上是相同,但隐藏了丑陋)
- 将加载逻辑移到 UI 之外并成为全局状态的一部分(Redux、MobX、Apollo 或任何其他状态管理库),这将符合关注点分离,并且应该使您的代码更具可读性。
- 最糟糕的情况是在加载内容时阻止您的用户执行任何操作 - 让您的应用看起来笨拙,但 React 不会再抱怨了。
最接近正确的方法是 2,但这会引发宗教辩论和一些 -燃烧,我不喜欢。
你可以参考这个:https://reactjs.org/docs/hooks-effect.html#effects-with-cleanup
您可以有一个变量来跟踪您的组件是否已卸载,let isMounted = true
在 useEffect
内,并在组件卸载后立即将其设置为 false
。
代码将是:
useEffect(() => {
let isMounted = true;
async function getPerMonthCharges(ownerPhoneNumber, noOfCars) {
console.log(`inside getPerMonthCharges`);
try {
const serviceProviderChargesDoc = await firestore().collection(`${serviceProviderId}_charges`).doc(`${ownerPhoneNumber}`).get()
if (serviceProviderChargesDoc?.data()?.chargesPerMonth > 0 && isMounted) { // add conditional check
setMonthlyCharge(serviceProviderChargesDoc?.data()?.chargesPerMonth)
return
}
} catch (error) {
console.log(`Error while fetching monthly charge ${error}`);
}
if (isMounted) setMonthlyCharge(noOfCars * perMonthGeneralCharge) // add conditional check
console.log(`done with getPerMonthCharges`);
}
getPerMonthCharges(ownerPhoneNumber, vehicles.length)
return () => { isMounted = false }; // cleanup toggles value, if unmounted
}, []);
这是我的示例 React 组件:
const OwnerView = () => {
const [monthlyCharge, setMonthlyCharge] = useState(0)
useEffect(() => {
getPerMonthCharges(ownerPhoneNumber, vehicles.length)
}, [])
async function getPerMonthCharges(ownerPhoneNumber, noOfCars) {
console.log(`inside getPerMonthCharges`);
try {
const serviceProviderChargesDoc = await firestore().collection(`${serviceProviderId}_charges`).doc(`${ownerPhoneNumber}`).get()
if (serviceProviderChargesDoc?.data()?.chargesPerMonth > 0) {
setMonthlyCharge(serviceProviderChargesDoc?.data()?.chargesPerMonth)
return
}
} catch (error) {
console.log(`Error while fetching monthly charge ${error}`);
}
setMonthlyCharge(noOfCars * perMonthGeneralCharge)
console.log(`done with getPerMonthCharges`);
}
}
即使在 getPerMonthCharges() 完成执行之前,OwnerView 也有可能被卸载。因此,如果 OwnerView 被卸载,我会收到一条警告,指出正在对未安装的组件进行状态更新,这是一个非操作。有人可以强调一下您的观察结果和编写这段代码的正确方法吗?
有很多方法可以解决这个问题
- 您可以检查组件是否仍处于挂载状态,我同意这种方法有点难看,但相当标准(我只使用
react-use
中的useAsync
之类的方法,这实际上是相同,但隐藏了丑陋) - 将加载逻辑移到 UI 之外并成为全局状态的一部分(Redux、MobX、Apollo 或任何其他状态管理库),这将符合关注点分离,并且应该使您的代码更具可读性。
- 最糟糕的情况是在加载内容时阻止您的用户执行任何操作 - 让您的应用看起来笨拙,但 React 不会再抱怨了。
最接近正确的方法是 2,但这会引发宗教辩论和一些 -燃烧,我不喜欢。
你可以参考这个:https://reactjs.org/docs/hooks-effect.html#effects-with-cleanup
您可以有一个变量来跟踪您的组件是否已卸载,let isMounted = true
在 useEffect
内,并在组件卸载后立即将其设置为 false
。
代码将是:
useEffect(() => {
let isMounted = true;
async function getPerMonthCharges(ownerPhoneNumber, noOfCars) {
console.log(`inside getPerMonthCharges`);
try {
const serviceProviderChargesDoc = await firestore().collection(`${serviceProviderId}_charges`).doc(`${ownerPhoneNumber}`).get()
if (serviceProviderChargesDoc?.data()?.chargesPerMonth > 0 && isMounted) { // add conditional check
setMonthlyCharge(serviceProviderChargesDoc?.data()?.chargesPerMonth)
return
}
} catch (error) {
console.log(`Error while fetching monthly charge ${error}`);
}
if (isMounted) setMonthlyCharge(noOfCars * perMonthGeneralCharge) // add conditional check
console.log(`done with getPerMonthCharges`);
}
getPerMonthCharges(ownerPhoneNumber, vehicles.length)
return () => { isMounted = false }; // cleanup toggles value, if unmounted
}, []);