为什么在return语句中,useEffect无法访问我的状态变量?
为什么在return语句中,useEffect无法访问我的状态变量?
我不明白为什么我的useEffect()
React函数无法访问我的组件状态变量。我试图在用户放弃在我们的应用程序中创建列表并导航到另一个页面时创建一个日志。我正在使用useEffect()
的return
方法来复制componentWillUnmount()
生命周期方法。你能帮忙吗?
代码示例
let[progress, setProgress] = React.useState(0) ... 用户开始构建他们的列表,导致进度递增 ... console.log(`useEffect外的进度: ${progress}`) useEffect(() => { return () => logAbandonListing() }, []) const logAbandonListing = () => { console.log(`useEffect内的进度: ${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
- react v 16.8.6
我真的很感谢您帮助我理解这里发生了什么。谢谢。
问题的出现的原因是因为在return语句中,useEffect无法访问状态变量。解决方法是通过将logAbandonedListing()包装在return语句之前的函数中,并将该函数作为useEffect的返回值,以模拟componentWillUnmount()的行为。
具体代码如下:
useEffect(() => { return () => { logAbandonedListing(); }; }, []);
这样,每当组件重新渲染时,该函数都会运行。可以在https://reactjs.org/docs/hooks-effect.html中了解更多关于useEffect的信息。
从我的理解来看,这实际上以一种类似于React基于类的组件中的componentWillMount()方法的方式调用了useEffect()。在logAbandonedListing()之前的return语句是关键,因为它改变了hook的行为,以模拟componentWillUnmount(),这正是我在这里寻找的解决方法。
我认为它同时模拟了componentDidMount()和componentWillMount(),如果你返回它,它的行为就类似于componentWillUnmount()。看到你找到了答案,这是典型的闭包。我也考虑过这一点,但最后认为componentWillUnmount()是一个问题。
解决方法:可以使用useReducer来避免闭包陈旧的问题,并且可以通过useReducer的dispatch函数来更新状态。
原因:在useEffect的返回函数中,当useEffect的依赖项是一个空数组[]时,无法访问状态变量。这是因为在组件卸载后,返回函数仍然可以执行,但此时状态变量已经不存在。
代码示例:
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{noteId}; } export default NoteEditor;
更多信息可以查看这个答案:[https://stackoverflow.com/questions/60456803](https://stackoverflow.com/questions/60456803)
为什么useEffect无法在return语句中访问我的状态变量?
我认为这是一个典型的陈旧闭包问题,刚开始很难理解。使用空的依赖数组,useEffect只会运行一次,并且它将从那次运行中访问状态。因此,它将从logAbandonListing函数中获得一个引用,从此刻开始,该函数也将访问状态。可以通过多种方式解决这个问题。
其中一种方法是将状态变量添加到依赖项中。
useEffect(() => { return () => logAbandonListing() }, [progress])
另一种解决方法是将状态值设置为引用。引用的引用不会发生变化,因此您将始终看到最新的值。
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只会运行一次。”这使我恍然大悟,非常感谢!我必须使用 useRef。使用 createRef 时,current 似乎是只读的。
很棒的答案,问题只是一个陈旧闭包问题。React hooks非常依赖闭包。除了useEffect钩子之外,如果您的新值基于由useState钩子定义的旧值,那么请确保将一个形如setNewState((currentValue)=> newValue)的函数传递给设置新状态,React将确保在更新时始终获得最新的状态值。
非常感谢,我正在使用状态来存储setInterval的引用,并遇到类似的问题。从变量中明显地命名,我从未想过使用引用。