【问题标题】:Why can't I update the state of my component from within a useEffect callback?为什么我不能在 useEffect 回调中更新组件的状态?
【发布时间】:2021-07-19 07:14:57
【问题描述】:

我正在尝试创建一个计时器,该计时器在应用程序处于后台时启动,然后当应用程序再次被调用到前台时,应用程序会检查它是否已经 15 分钟(为了便于测试,我使用的是 15s现在)。如果应用程序将用户注销,则计时器会重置。

如果这是一个骗子,请转介我 - 我只是找不到任何东西。这几天我一直在努力解决这个问题。

这是我的想法:

  • 我在 useEffect 中使用 AppState.addEventListener 监听应用程序状态变化。这会在更改时触发一个回调函数,允许我访问它已更改为的实际状态。
  • 我在 setState 挂钩中存储用户将应用程序置于后台时的 Date.now()。
  • 当应用程序被调用到前台时,我会使用 Date.now() 减去存储的状态来计算时间是否已过。

这是我的问题:

  • 当我尝试在 useEffect 回调中使用 setState 重置计时器时,它什么也不做。我想不出我应该如何更新状态来做到这一点。我也尝试将这些调用作为对其他函数的引用,它做同样的事情

注意事项:

  • 这是在我的根应用导航器上处理的,用户已通过身份验证。很确定这无关紧要,但以防万一有人想知道。
  • 我曾尝试使用 redux 进行此操作,但问题相同。不执行分派状态更新。
  • 我正在使用 expo 管理的工作流程,因此如果您有任何替代的 npm 建议,它们需要兼容。

const rootNavigator = () => {

  const dispatch = useDispatch()
  const [loginTimer, setLoginTimer] = useState(Date.now())

  useEffect(() => {
    AppState.addEventListener('change', handleAppStateChange)

    return (() => {
      AppState.removeEventListener('change', handleAppStateChange)
    })
  }, [])

  const handleAppStateChange = (state) => {

    if (state === "inactive" || state === "background") {
        setLoginTimer(Date.now())
      }
    if (state === "active") {
      if (Date.now() - loginTimer < 15000) {
        setLoginTimer(Date.now())
      } else if (Date.now() - loginTimer >= 15000) {
          dispatch(logout())
      }
    }
  }


  return (
    <SomeComponent />
    ...
  ) 

}

我几乎可以肯定这是我遇到的一个非常基本的错误,因为我对如何处理这个问题的理解还不够完善。

提前致谢。

【问题讨论】:

  • 是否发生任何错误?你确定handleAppStateChange 正在运行吗?
  • @iagowp 没有错误。我可以验证 handleAppStateChange 是否正在运行 console.log state 变量,并且每次都正确记录。

标签: javascript reactjs react-native date setstate


【解决方案1】:

问题出在您在回调函数handleAppStateChange() 中对useState 数据的引用。 AppState.addEventListener 的回调是在组件挂载时注册的,但每次调用时都会为该回调使用当前状态的快照,因此它不会以您希望的方式进行响应。

好消息是有很多方法可以解决这个问题,其中一些在this great answer here 中有详细的解释。

【讨论】:

  • 非常感谢。在一系列场景中超级有用且详细。这是一个我相信我会一次又一次地回来的帖子。
【解决方案2】:

我的解决方案,使用 Jacob K 链接中的信息。最后它非常简单,我把它归结为一个更简洁的陈述。可能可以使用三元,但这很好用。


const rootNavigator = () => {

  const dispatch = useDispatch()
  const timer = useRef(Date.now())

  useEffect(() => {
    AppState.addEventListener('change', handleAppStateChange)

    return (() => {
      AppState.removeEventListener('change', handleAppStateChange)
    })
  }, [])

  const handleAppStateChange = (state) => {
    if (state === "inactive" || state === "background") {
      timer.current = Date.now()
    }
    if (state === "active" && (Date.now() - timer.current > 15000)) {
      dispatch(logout())
    }
  }


  return (
    <SomeComponent />
    ...
  ) 

}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-01-16
    • 1970-01-01
    • 2022-11-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-23
    相关资源
    最近更新 更多