Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.1k views
in Technique[技术] by (71.8m points)

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

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 the logAbandonListing() function
  • The first console.log() (above useEffect) logs something greater than 0 for the progress state
  • The second console.log() logs 0 for the progress state, disabling the code to return true for the if statement and reach the addToLog() 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.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...