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

Dav*_*nes 7 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)
    }
  }
Run Code Online (Sandbox Code Playgroud)

预期行为

代码将达到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
  • 反应 v 16.8.6

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

Pet*_*uzs 13

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

对于空的依赖数组,useEffect 将只运行一次。它将从那一次运行访问状态。所以从这一刻起,它将有一个来自 logAbandonListing 函数的引用。此函数也将从这一刻开始访问状态。您可以通过多种方式解决问题。

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

  useEffect(() => {
    return () => logAbandonListing()
  }, [progress])
Run Code Online (Sandbox Code Playgroud)

另一种解决方案是将状态值设置为 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)
    }
  }
Run Code Online (Sandbox Code Playgroud)

如果 userId 也在更改,那么您应该将其添加到依赖项或引用中。


小智 7

要在依赖项为空数组useEffect的返回函数中对状态的当前值执行某些操作,您可以使用. 这样您就可以避免过时的关闭问题并从 的函数更新状态。useEffects[]useReduceruseReducerdispatch

例子是:

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;
Run Code Online (Sandbox Code Playgroud)

有关此答案的更多信息