繁体   English   中英

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

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

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

代码示例

  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() function
  • 第一个console.log() (在useEffect上方)记录大于 0 的progress state
  • 第二个console.log()progress state 记录 0,禁用代码为if语句返回true并到达addToLog() function。

环境

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

我真的很感激一些帮助理解这里发生了什么。 谢谢。

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

使用空依赖数组,useEffect 将只运行一次。 它将从那一次运行中访问 state。 所以从这一刻起它就会有来自 logAbandonListing function 的引用。 从这一刻起,这个 function 也将访问 state。 您可以通过多种方式解决问题。

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

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

另一种解决方案是将 state 值设置为参考值。 并且 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的返回 function 中的状态当前值中执行某些操作,其中useEffects依赖项是空数组[] ,您可以使用useReducer 这样,您可以避免过时的关闭问题并从useReducerdispatch function 更新 state。

示例是:

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返回 function 时,它的行为类似于componentWillUnmount所以我认为它只在清理时运行。 您实际上需要调用logAbandonListing ,例如:

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

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

它写得非常好。

我尝试使用这个沙盒来解释我的答案。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM