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

IT技术 reactjs next.js use-effect
2021-04-14 02:41:33

我不明白为什么我的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)记录了大于 0 的progress状态
  • 第二个console.log()用于日志0progress状态,禁用代码返回trueif语句,并达到addToLog()功能。

环境

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

我真的很感激一些帮助理解这里发生的事情。谢谢。

4个回答

我认为这是一个典型的陈旧关闭问题。而且一开始很难理解。

对于空的依赖数组,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 也在更改,那么您应该将其添加到依赖项或引用中。

太棒了,谢谢,彼得。将变量作为依赖项添加到 useEffect() 调用很有魅力。
2021-05-25 02:41:33
你好,希望你很好,你能看看这个问题吗?非常感谢您的帮助。stackoverflow.com/questions/65759907/...
2021-05-25 02:41:33
-“对于空的依赖数组,useEffect 将只运行一次。” 这让它在我脑海中响起,非常感谢!
2021-05-26 02:41:33
我不得不使用useRef. 随着createRef.current似乎只读。
2021-06-15 02:41:33

useEffectuseEffects依赖项为空数组's return 函数中的状态当前值中执行某些操作[],您可以使用useReducer. 通过这种方式,您可以避免过时的关闭问题并从useReducerdispatch函数更新状态

示例是:

import React, { useEffect, useReducer } from "react";

function reducer(state, action) {
  switch (action.type) {
    case "set":
      return action.payload;
    case "unMount":
      console.log("This note has been closed: " + state); // This note has been closed: 201
      break;
    default:
      throw new Error();
  }
}

function NoteEditor({ initialNoteId }) {
  const [noteId, dispatch] = useReducer(reducer, initialNoteId);

  useEffect(function logBeforeUnMount() {
    return () => dispatch({ type: "unMount" });
  }, []);


  return <div>{noteId}</div>;
}
export default NoteEditor;

关于这个答案的更多信息

当你从 返回一个函数时useEffect,它的行为就像componentWillUnmount我认为它只在清理时运行。您实际上需要logAbandonListing像这样调用

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

所以每次组件重新渲染时它都会运行。您可以useEffecthttps://reactjs.org/docs/hooks-effect.html阅读更多信息

写的太好了

据我了解,这实际上以反映基于 React 类的组件中的 componentWillMount() 的方式调用 useEffect()。logAbandonedListing() 之前的 return 是关键,因为它改变了钩子的行为方式来反映 componentWillUnmount(),这就是我在这里寻找的。
2021-05-27 02:41:33
componentDidMount()componentWillMount()我想的一样好,如果你回来,它的行为就像componentWillUnmount()看到你找到了答案。典型的关闭。我也考虑过,但最后认为componentWillUnmount()是一个问题。
2021-05-28 02:41:33
你好,希望你很好,你能看看这个问题吗?非常感谢您的帮助。stackoverflow.com/questions/65759907/...
2021-06-21 02:41:33

我尝试使用这个沙箱来解释我的答案。 在此处输入链接描述

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