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

Why can't useEffect access my state variable in a return statement?

我不明白为什么我的 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 递增,然后离开页面时,就会发生这种情况。

环境

如果能帮助我理解这里发生的事情,我将不胜感激。谢谢。

当你 return 来自 useEffect 的函数时,它的行为类似于 componentWillUnmount 所以我认为它只在清理时运行。你需要像这样调用 logAbandonListing

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

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

上阅读有关 useEffect 的更多信息

写的真好

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

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

我认为这是一个典型的陈旧关闭问题。起初很难理解。

对于空的依赖数组,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 的 return 函数中对状态的当前值执行某些操作,其中 useEffects 依赖项是空数组 [],您可以使用 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;

更多信息