为什么useEffect无法在return语句中访问我的状态变量?

时间:2020-05-22 14:05:27

标签: reactjs next.js use-effect

我不明白为什么我的useEffect() React函数无法访问组件的状态变量。当用户放弃在我们的应用中创建列表并导航到另一个页面时,我正在尝试创建日志。我正在使用useEffect() return方法来复制componentWillUnmount()生命周期方法。你能帮忙吗?

代码示例

  let[progress, setProgress] = React.useState(0)

  ... user starts building their listing, causing progress to increment ...

  console.log(`progress outside useEffect: ${progress}
  useEffect(() => {
    return () => logAbandonListing()
  }, [])
  const logAbandonListing = () => {
    console.log(`progress inside: ${progress}`)
    if (progress > 0) {
      addToLog(userId)
    }
  }

预期行为

代码将到达addToLog(),从而导致记录此行为。

观察到的行为

当用户在列表中输入内容,导致progress递增,然后离开页面时,就会发生这种情况。

  • useEffect()方法工作完美,并触发了logAbandonListing()函数
  • 对于console.log()状态,第一个useEffect(在progress上方)记录的值大于0
  • 第二个console.log()progress状态记录为0,从而使代码无法返回true语句的if并到达addToLog()函数。 / li>

环境

  • 使用在Firefox 76.0.1中运行的Next.js构建的应用程序的本地开发环境
  • nextjs v 8.1.0
  • 反应v 16.8.6

我非常感谢您的帮助,以帮助您了解这里的情况。谢谢。

4 个答案:

答案 0 :(得分:6)

我认为这是一个典型的过时关闭问题。一开始很难理解。

使用空的依赖项数组,useEffect将仅运行一次。并且它将从那一次运行中访问状态。因此,此刻它将从logAbandonListing函数获得引用。此功能还将从此刻开始访问状态。您可以通过多种方式解决问题。

其中之一是将状态变量添加到您的依赖项中。

  useEffect(() => {
    return () => logAbandonListing()
  }, [progress])

另一种解决方案是将状态值设置为ref。而且ref的引用没有改变,因此您将始终看到最新鲜的值。

let[progress, setProgress] = React.useState(0);
const progressRef = React.createRef();
progressRef.current = progress;

...

  const logAbandonListing = () => {
    console.log(`progress inside: ${progressRef.current}`)
    if (progressRef.current > 0) {
      addToLog(userId)
    }
  }

如果userId也发生变化,则应将其添加到依赖项或引用中。

答案 1 :(得分:2)

要在useEffect依赖项是空数组useEffects的{​​{1}}返回函数中使用状态的当前值进行操作,可以使用[]。这样,您可以避免过时的关闭问题,并可以通过useReducer的{​​{1}}函数来更新状态。

示例为:

useReducer

有关此answer

的更多信息

答案 2 :(得分:0)

当您从useEffect返回一个函数时,它的行为类似于componentWillUnmount,所以我认为它仅在清理时运行。您实际上需要像这样呼叫logAbandonListing

useEffect(() => {
  logAbandonListing();
}, []);

因此它在每次重新渲染组件时运行。您可以在https://reactjs.org/docs/hooks-effect.html

上了解有关useEffect的更多信息

写得很好。

答案 3 :(得分:0)

我尝试使用此沙箱来解释我的答案。 enter link description here

基本上,您是从useEffect回调返回一个函数。但是从未真正调用过该返回的函数,因此它实际上并未执行,因此记录了放弃操作。如果您查看沙盒中的代码,则之后我添加了包装器Parens和(),以实际上导致方法的调用导致console.log的执行。