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

Posted

技术标签:

【中文标题】为啥 useEffect 不能在 return 语句中访问我的状态变量?【英文标题】:Why can't useEffect access my state variable in a return statement?为什么 useEffect 不能在 return 语句中访问我的状态变量? 【发布时间】:2020-09-09 09:36:45 【问题描述】:

我不明白为什么我的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()progress 状态记录0,禁用代码为if 语句返回true 并到达addToLog() 函数。

环境

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

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

【问题讨论】:

【参考方案1】:

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

使用空依赖数组,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() 调用很有效。 -“使用空的依赖数组,useEffect 将只运行一次。” 这让它在我的脑海中点击,非常感谢! 你好,希望你好,请你看看这个问题。非常感谢您的帮助。***.com/questions/65759907/… 我必须使用useRef。对于createRef.current 似乎是只读的。【参考方案2】:

要在useEffect 的返回函数中的状态当前值中执行某些操作,其中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;

更多信息answer

【讨论】:

【参考方案3】:

当你从useEffect 返回一个函数时,它的行为类似于componentWillUnmount,所以我认为它只在清理时运行。您实际上需要致电logAbandonListing,例如:

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

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

写得真好。

【讨论】:

据我了解,这实际上调用 useEffect() 的方式反映了基于 React 类的组件中的 componentWillMount()。 logAbandonedListing() 之前的返回是关键,因为这会改变钩子的行为方式以镜像 componentWillUnmount(),这正是我在这里寻找的。​​span> 它反映了componentDidMount() 以及componentWillMount() 我认为&如果你返回它的行为就像componentWillUnmount()。看到你找到了答案。典型的关闭。我也想过,但最后认为componentWillUnmount() 是个问题。 你好,希望你好,请你看看这个问题。非常感谢您的帮助。***.com/questions/65759907/…【参考方案4】:

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

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

【讨论】:

以上是关于为啥 useEffect 不能在 return 语句中访问我的状态变量?的主要内容,如果未能解决你的问题,请参考以下文章

状态在 useEffect 中没有更新,为啥?

为啥在组件加载时不调用 useEffect(()=...,[]) ?

为啥每次渲染都会调用 `useEffect` 的清理功能?

为啥 useState 钩子在 useEffect() 中使用循环时不更新

为啥 useEffect 钩子在第一次渲染时不调用函数?

基本反应问题。为啥在 React 文档的这个例子中需要 useEffect ?