如何取消firebase的useEffect订阅

Posted

技术标签:

【中文标题】如何取消firebase的useEffect订阅【英文标题】:how to cancel useEffect subscriptions of firebase 【发布时间】:2021-03-23 17:46:29 【问题描述】:

我不太明白 useEffect 清理功能是如何工作的。因为无论我做什么都会收到警告:

警告:无法对未安装的组件执行 React 状态更新。这是一个空操作,但它表明您的应用程序中存在内存泄漏。要解决此问题,请在 useEffect 清理函数中取消所有订阅和异步任务。

这是我的代码:

useEffect(() => 
        setLoading(true) 

        // Get position list
       const getPositionList = db.collection('lists').doc('positions').get()
            .then( res => 
                let data = JSON.stringify(res.data())
                data = JSON.parse(data)
                setPositionsList(data.list)
                setLoading(false)
            )
        return () => getPositionList
    , [])

【问题讨论】:

您在未安装的组件上更改了没有意义的状态,您会收到警告。 你能解释更多吗? 【参考方案1】:

useEffect 期望取消订阅/清理函数或 null 作为返回值。您的 .get() 不是订阅,因此无需清理

但是 useEffect 不是异步的,并且 .get() 明确地返回一个需要延迟的承诺,即使使用 .then() 也是如此。你的依赖数组是空的,所以 useEffect 只被调用一次——但我怀疑你的组件在 .get() 返回之前卸载了。

您的代码需要更接近:

useEffect(() => 
        setLoading(true) 
       
        (async () => 
        // Get position list
           await db.collection('lists').doc('positions').get()
            .then( res => 
                let data = JSON.stringify(res.data())
                data = JSON.parse(data)
                setPositionsList(data.list)
                setLoading(false)
            );
        )();
        return null
    , [])

( async () => )() 创建一个匿名异步函数,然后执行它。您确实想尝试构建您的系统,以使封闭组件在加载设置为 false 之前不会卸载 - 我们在这里看不到,所以我们必须假设您这样做 that 部分是正确的。

重要的收获:

useEffect 不是异步的 .get() 是异步的 ( async () => ) 创建匿名异步函数,( async () => )() 创建自执行匿名异步函数

【讨论】:

【参考方案2】:
useEffect(() => 
        setLoading(true) 

       // below firebase api return promise. hence no need to unsubscribe.
       db.collection('lists').doc('positions').get()
            .then( res => 
                let data = JSON.stringify(res.data())
                data = JSON.parse(data)
                setPositionsList(data.list)
                setLoading(false)
            )
        return () => 
          // any clean up code, unsubscription goes here. unmounting phase
        
    , [])

【讨论】:

【参考方案3】:

根据@Dennis Vash 的回答我发现,在设置状态之前我必须检查组件是否已安装,所以我在useEffect 中添加变量,在设置状态之前我检查我添加if 语句:

useEffect(() => 
        setLoading(true) 
        let mounted = false // <- add variable
        // Get position list
        db.collection('lists').doc('positions').get()
            .then( res => 
                let data = JSON.stringify(res.data())
                data = JSON.parse(data)
                if(!mounted) // <- check is it false
                    setPositionsList(data.list)
                    setLoading(false)
                
            )
        return () => 
            mounted = true // <- change to true 
        
    , [])

【讨论】:

以上是关于如何取消firebase的useEffect订阅的主要内容,如果未能解决你的问题,请参考以下文章

在 Context.Consumer 创建的 useEffect 清理函数中取消所有订阅

Firebase 如何取消订阅

如何成功取消订阅用户通知(firebase,react)

订阅和取消订阅主题 - firebase

在一个事件中在后台触发本机 Firebase 多个通知

Firebase 云消息传递为已取消订阅的令牌返回 200 OK