在 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 被卸载,我会收到一条警告,指出正在对未安装的组件进行状态更新,这是一个非操作。有人可以强调一下您的观察结果和编写这段代码的正确方法吗?

有很多方法可以解决这个问题

  1. 您可以检查组件是否仍处于挂载状态,我同意这种方法有点难看,但相当标准(我只使用 react-use 中的 useAsync 之类的方法,这实际上是相同,但隐藏了丑陋)
  2. 将加载逻辑移到 UI 之外并成为全局状态的一部分(Redux、MobX、Apollo 或任何其他状态管理库),这将符合关注点分离,并且应该使您的代码更具可读性。
  3. 最糟糕的情况是在加载内容时阻止您的用户执行任何操作 - 让您的应用看起来笨拙,但 React 不会再抱怨了。

最接近正确的方法是 2,但这会引发宗教辩论和一些 -燃烧,我不喜欢。

你可以参考这个:https://reactjs.org/docs/hooks-effect.html#effects-with-cleanup

您可以有一个变量来跟踪您的组件是否已卸载,let isMounted = trueuseEffect 内,并在组件卸载后立即将其设置为 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
}, []);