OiO.lk Community platform!

Oio.lk is an excellent forum for developers, providing a wide range of resources, discussions, and support for those in the developer community. Join oio.lk today to connect with like-minded professionals, share insights, and stay updated on the latest trends and technologies in the development field.
  You need to log in or register to access the solved answers to this problem.
  • You have reached the maximum number of guest views allowed
  • Please register below to remove this limitation

Why does the action handler function always capture the initial value of `useState`? [duplicate]

  • Thread starter Thread starter 刘maxwell
  • Start date Start date

刘maxwell

Guest
I have view where I want to add a scroll action listener to. I want to get the current scroll offset and compare its previous value to do something. Here is code:

Code:
const StickyBox = () => {
  const [offset, setOffset] = useState(500); // inital offset
  const handleScroll = () => {
    const scrollY = window.scrollY || window.pageYOffset;
    console.log(offset);  // always 500!
    setOffset(scrollY);    
    if (scrollY > offset) { ... }
  }
  useEffect(() => {
    window.addEventListener('scroll', handleScroll);
    return () => { window.removeEventListener(....); }
  }, []);

  return <div className="...">{offset}</div>
}

Now I know the right way to do this is to get the previous state in the useState parameter with a function.

Code:
setOffset((prev) => {
  if (scrollY > prev) {...}
})

But still I don't understand how JS closure work and how React useState work. I can see that:

  1. useEffect has a dependency of [] so it only invokes once. When I scroll the view, handleScroll gets called and setOffset gets called.
  2. Since setOffset gets called, the component re-rendered, that is, the whole StickyBox function runs again, handleScroll function object is created again.

Q: 1. At this moment, handleScroll function just capture the offset variable, and the useState is expected to remember the value in its previous render, why console.log always output its initial value 500?

  1. Since handleScroll is created on every render but useEffect just invokes once, does that mean, event listener just calls the initial handleScroll function over its lifecycle? Is such "function re-creation" considered to be good practice? Or because function object is ref type in heap, so each time the thing re-created is just a new stack variable that points to the same heap memory?

<p>I have view where I want to add a scroll action listener to. I want to get the current scroll offset and compare its previous value to do something. Here is code:</p>
<pre><code>const StickyBox = () => {
const [offset, setOffset] = useState(500); // inital offset
const handleScroll = () => {
const scrollY = window.scrollY || window.pageYOffset;
console.log(offset); // always 500!
setOffset(scrollY);
if (scrollY > offset) { ... }
}
useEffect(() => {
window.addEventListener('scroll', handleScroll);
return () => { window.removeEventListener(....); }
}, []);

return <div className="...">{offset}</div>
}
</code></pre>
<p>Now I know the right way to do this is to get the previous state in the <code>useState</code> parameter with a function.</p>
<pre><code>setOffset((prev) => {
if (scrollY > prev) {...}
})
</code></pre>
<p>But still I don't understand how JS closure work and how React <code>useState</code> work.
I can see that:</p>
<ol>
<li>useEffect has a dependency of <code>[]</code> so it only invokes once. When I scroll the view, <code>handleScroll</code> gets called and <code>setOffset</code> gets called.</li>
<li>Since <code>setOffset</code> gets called, the component re-rendered, that is, the whole <code>StickyBox</code> function runs again, <code>handleScroll</code> function object is created again.</li>
</ol>
<p>Q: 1. At this moment, <code>handleScroll</code> function just capture the <code>offset</code> variable, and the <code>useState</code> is expected to remember the value in its previous render, why <code>console.log</code> always output its initial value 500?</p>
<ol start="2">
<li>Since <code>handleScroll</code> is created on every render but <code>useEffect</code> just invokes once, does that mean, event listener just calls the initial <code>handleScroll</code> function over its lifecycle? Is such "function re-creation" considered to be good practice? Or because function object is ref type in heap, so each time the thing re-created is just a new stack variable that points to the same heap memory?</li>
</ol>
 

Latest posts

Online statistics

Members online
0
Guests online
2
Total visitors
2
Ads by Eonads
Top