【问题标题】:Am I using useRef correctly?我正确使用 useRef 吗?
【发布时间】:2020-03-08 08:24:59
【问题描述】:

我想卸载我的函数以防止内存泄漏。我一开始是声明 mount 的初始值,将其设置为 true,然后在 useEffect 中将其声明为 false,但我收到了一个丑陋的警告,建议我实现 useRef。我只是想确保我正确使用它并正确卸载我的 useEffect 中的功能。为了澄清,我试图在卸载组件后卸载地理定位侦听器。

import React, { useState, useEffect, useRef } from "react"

const initialLocationState = {
  latitude: null,
  longitude: null,
  speed: null
}

const App = () => {
  const [count, setCount] = useState(0)
  const [isOn, setIsOn] = useState(false)
  const [mousePosition, setMousePosition] = useState({ x: null, y: null })
  const [status, setStatus] = useState(navigator.onLine)
  const [{ latitude, longitude, speed }, setLocation] = useState(
    initialLocationState
  )
  const DidMountRef = useRef(true)

  useEffect(() => {
    document.title = `You have clicked ${count} times`
    window.addEventListener("mousemove", handleMouseMove)
    window.addEventListener("onine", handleOnline)
    window.addEventListener("offline", handleOffline)
    navigator.geolocation.getCurrentPosition(handleGeoLocation)
    const watchId = navigator.geolocation.watchPosition(handleGeoLocation)
    return () => {
      window.removeEventListener("mousemove", handleMouseMove)
      window.removeEventListener("onine", handleOnline)
      window.removeEventListener("offline", handleOffline)
      navigator.geolocation.clearWatch(watchId)
      DidMountRef.current = false
    }
  }, [count])

  const handleOnline = () => {
    setStatus(true)
  }

  const handleOffline = () => {
    setStatus(false)
  }

  const handleMouseMove = event => {
    setMousePosition({
      x: event.pageX,
      y: event.pageY
    })
  }

  const incrementCount = () => {
    setCount(prevCount => prevCount + 1)
  }

  const toggleLight = () => {
    setIsOn(previsOn => !previsOn)
  }

  const handleGeoLocation = event => {
    if (DidMountRef) {
      setLocation({
        latitude: event.coords.latitude,
        longitude: event.coords.longitude,
        speed: event.coords.speed
      })
    }
  }

  return (
    <>
      <h2>Counter</h2>
      <button onClick={incrementCount}>I was clicked {count} times</button>
      <h2>Toggle Light</h2>
      <img
        src={
          isOn
            ? "https://icon.now.sh/highlight/fd0"
            : "https://icon.now.sh/highlight/aaa"
        }
        style={{
          height: "50px",
          width: "50px"
        }}
        onClick={toggleLight}
        alt='Lightbulb'
      ></img>
      <h2>Mouse Position</h2>
      {JSON.stringify(mousePosition, null, 2)}
      <br />
      <h2>Network Status</h2>
      <p>
        you are <strong>{status ? "online" : "offline"}</strong>
      </p>
      <h2>GeoLocation</h2>
      <p>Latitude is {latitude}</p>
      <p>longitude is {longitude}</p>
      <p>Your speed is {speed ? speed : "0"}</p>
    </>
  )
}

export default App

【问题讨论】:

  • 你能告诉我你想用 ref 完成什么吗?一旦计数增加,您是否试图不再听位置?因为看起来这就是你现在正在做的事情。
  • 我试图在组件卸载后删除地理位置监听器
  • 你能告诉我你得到的警告,告诉你使用useRef吗?
  • 我想你在这里是在把帽子戴在帽子上。您正在清除手表并将DidMount 同时设置为false,然后在watchPosition 的处理程序中检查DidMount 是否为false,该处理程序永远不会被调用。我认为您根本不需要 ref (或 mounted 变量)。我是不是误会了什么?
  • useEffect 的清理工作应该会解决这个问题。虽然,请注意,如果句柄函数不是纯函数(如果它取决于状态或道具等),您可能会删除不同的侦听器,然后您可能会发生泄漏。在你的情况下,一切看起来都很好。

标签: reactjs


【解决方案1】:

几个cmets:

  const DidMountRef = useRef(true)

  useEffect(() => {
    ...
  }

从技术上讲,在调用 useEffect 回调之前不会安装组件。但这似乎对您示例中的逻辑影响不大。


  useEffect(() => {
    navigator.geolocation.getCurrentPosition(handleGeoLocation)
    const watchId = navigator.geolocation.watchPosition(handleGeoLocation)
    return () => {
      navigator.geolocation.clearWatch(watchId)
      DidMountRef.current = false
    }
  }, [count])

我不认为您真的想要 [count] 作为挂钩依赖项。如果您只想在挂载/卸载时调用效果,则应提供 [] 作为依赖项。来自React Docs

如果您只想运行一个效果并只清理一次(在挂载和卸载时),您可以传递一个空数组 ([]) 作为第二个参数。


我真的不明白你为什么要守在handleGeoLocation

  const handleGeoLocation = event => {
    if (DidMountRef) {
      ...
    }
  }

这段代码应该可以工作:

  useEffect(() => {
    navigator.geolocation.getCurrentPosition(handleGeoLocation)
    const watchId = navigator.geolocation.watchPosition(handleGeoLocation)
    return () => {
      navigator.geolocation.clearWatch(watchId)
    }
  }, [])

  const handleGeoLocation = event => {
    setLocation({
      latitude: event.coords.latitude,
      longitude: event.coords.longitude,
      speed: event.coords.speed
    })
  }

【讨论】:

    猜你喜欢
    • 2021-05-09
    • 2014-02-28
    • 2015-06-25
    • 2022-01-08
    • 2011-08-07
    • 2011-09-26
    • 2019-05-03
    • 2016-03-12
    • 1970-01-01
    相关资源
    最近更新 更多