【问题标题】:React useEffect dependencies for subscribe/unsubscribe with eslint exhaustive-deps使用 eslint 详尽的deps 反应订阅/取消订阅的 useEffect 依赖项
【发布时间】:2020-04-27 11:33:32
【问题描述】:

我有一个useEffect 钩子,它应该在组件出现时订阅地理位置更新,然后在组件消失时取消订阅。所以我将[] 作为效果依赖项传递,因为我只希望它在挂载/卸载时运行。

import { useWatchPosition } from "@ionic/react-hooks/geolocation"
import React, { useEffect } from "react"

function SiteMap() {
  const { currentPosition, startWatch, clearWatch } = useWatchPosition()

  // Subscribe/Unsubscribe to geo location on component mount/unmount.
  useEffect(() => {
    startWatch()
    return clearWatch
  }, [])

  return <svg>{/* ... */}</svg>
}

导致 eslint 发出警告的原因:

React Hook useEffect 缺少依赖项:clearWatchstartWatch。要么包含它们,要么移除依赖array.eslint(react-hooks/exhaustive-deps)

所以我把它改成了这样:

  useEffect(() => {
    startWatch()
    return clearWatch
  }, [startWatch, clearWatch])

这会导致无限渲染循环。

我猜无限循环是由 @ionic/react-hooks/geolocation 库引起的,该库在每次调用 useWatchPosition() 时都会创建新函数,从而使依赖项看起来陈旧。

所以我应该通过以下方式禁用对这一行的检查:

// eslint-disable-next-line react-hooks/exhaustive-deps

或者有什么方法让我在这里做正确的事情吗?

【问题讨论】:

    标签: javascript reactjs ionic-framework react-hooks


    【解决方案1】:

    阅读useWatchPositionsource code,可以看到函数不是用useCallback创建的,这意味着每当调用钩子时都会重新生成它们。

    您可以在ref 中存储对函数的引用,并使用ref.current 调用函数:

    function SiteMap() {
      const { currentPosition, startWatch, clearWatch } = useWatchPosition()
      
      const startWatchRef = useRef(startWatch)
      const clearWatchRef = useRef()
    
      useEffect(() => {
        clearWatch.current = clearWatch // the updated clearWatch
      })
    
      // Subscribe/Unsubscribe to geo location on component mount/unmount.
      useEffect(() => {
        startWatchRef.current()
                
        return () => clearWatchRef.current()
      }, [])
    
      return <svg>{/* ... */}</svg>
    }
    

    【讨论】:

    • 不会在每次渲染后立即调用clearWatch,但从不多次调用startWatch
    • 不,因为clearWatch 作为组件卸载时调用的函数返回。它在没有被调用的情况下返回 - return clearWatch.
    • 等等,我认为只要依赖项发生更改卸载,就会调用效果清理函数。清理函数只会在卸载时调用?
    • 你是对的。在这种情况下,每次重新渲染都会调用它。我已经更新了答案。请注意,由于已知 ref 是静态的,因此您甚至不需要将它们包含在依赖项列表中(尽管包含它们并不重要)。
    • 更新 - 在调用startWatch 之后,clearWatch 函数应该包含更新的watchId,并设置watchId,因此您应该在组件重新呈现时更新clearWatchRef.current。不要在组件中多次调用startWatch
    猜你喜欢
    • 1970-01-01
    • 2020-10-17
    • 2021-03-23
    • 2019-11-26
    • 1970-01-01
    • 2019-10-26
    • 2022-01-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多