问题
I don't understand why my useEffect()
React function can't access my Component's state variable. I'm trying to create a log when a user abandons creating a listing in our app and navigates to another page. I'm using the useEffect()
return
method of replicating the componentWillUnmount()
lifecycle method. Can you help?
Code Sample
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)
}
}
Expected Behavior
The code would reach addToLog()
, causing this behavior to be logged.
Observed Behavior
This is what happens when a user types something into their listing, causing progress
to increment, and then leaves the page.
- The
useEffect()
method works perfectly, and fires thelogAbandonListing()
function - The first
console.log()
(aboveuseEffect
) logs something greater than 0 for theprogress
state - The second
console.log()
logs 0 for theprogress
state, disabling the code to returntrue
for theif
statement and reach theaddToLog()
function.
Environment
- Local dev environment of an app built with Next.js running in Firefox 76.0.1
- nextjs v 8.1.0
- react v 16.8.6
I'd really appreciate some help understanding what's going on here. Thanks.
回答1:
I think it is a typical stale closure problem. And it is hard to understand at first.
With the empty dependency array the useEffect will be run only once. And it will access the state from that one run. So it will have a reference from the logAbandonListing function from this moment. This function will access the state from this moment also. You can resolve the problem more than one way.
One of them is to add the state variable to your dependency.
useEffect(() => {
return () => logAbandonListing()
}, [progress])
Another solution is that you set the state value to a ref. And the reference of the ref is not changing, so you will always see the freshest value.
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)
}
}
If userId is changing too, then you should add it to the dependency or a reference.
回答2:
When you return a function from useEffect
, it behaves like componentWillUnmount
so I think it only runs while cleaning up. You'd need to actually call logAbandonListing
like:
useEffect(() => {
logAbandonListing();
}, []);
So it runs everytime a component re-renders. You can read more about useEffect
on https://reactjs.org/docs/hooks-effect.html
It's written excellently.
回答3:
I tried using this sandbox to explain my answer. enter link description here
Basically you are returning a function from your useEffect Callback. But that returned function is never really invoked so it does no actually execute and thus log the abandon action. If you look at the Code in the sandbox I have added a wrapper Parens and () afterwards to actually cause the method to be invoked leading to console.log executing.
来源:https://stackoverflow.com/questions/61956823/why-cant-useeffect-access-my-state-variable-in-a-return-statement