1

I am going to update state when I scrolled bottom of a div. I have succeeded to detect user scrolled bottom of div but updating the state always logs initial value. Here is my code.

const ExampleApp = () => {
  const [value, setValue] = useState(0);
  useEffect(() => {
        document.addEventListener('scroll', trackScrolling);
        return () => {
            document.removeEventListener('scroll', trackScrolling);
        }
    }, []);
  const trackScrolling = () => {
    const wrappedElement = document.getElementById('vContainer');
    if (isBottom(wrappedElement)) {
        // Here user scrolled to bottom of a div
        console.log('Log Value: ', value); // I want to see the increased value, but always logs 0 every time I scrolled bottom.
        setValue(value + 1);
    }
  }
}

I am curious why the value NOT increased every time I detect bottom of a view. And what is the solution to increase the state value when I detect bottom. Thanks in advance.

daniels
  • 47
  • 1
  • 7
  • Well... function ```isBottom``` is defined in other place and I am sure detecting of bottom is works well. When I have detected the bottom of a view, I am trying to increase state value. But it always logs 0 every time I hit the bottom of a view. – daniels Feb 25 '21 at 19:27
  • I just tried your sample code but it always logs 0. Is it logs increased value on your side? – daniels Feb 25 '21 at 19:37
  • Well... yes. But can you see the console logs? It always logs value 0 every time I scroll up and hit bottom. Also the value in browser is always 1. Why it does not getting increased? – daniels Feb 25 '21 at 19:45
  • Applying this method may also be queuing. – A.R.SEIF Feb 25 '21 at 20:19

2 Answers2

1

It is happening because of closure.

With document.addEventListener('scroll', trackScrolling), the function property trackScrolling is getting attached with listener, binded with the initial value i.e. 0.

Check this snippet: (don't click on full page when trying the snippet or you won't see scroll)

function App() {

  const [value, setValue] = React.useState(0)

  function isBottom(e) { return true }

  React.useEffect(() => {
    document.addEventListener('scroll', trackScrolling)
    return () => {
      document.removeEventListener('scroll', trackScrolling)
    }
  }, [])

  const trackScrolling = () => {
    const wrappedElement = document.getElementById('vContainer')

    if (isBottom(wrappedElement)) {
      console.log('Log Value1: ', value)
      setValue(prev => { 
        console.log('Log Value2: ', prev + 1);
        return prev + 1;
       })
    }
  }
  return <div id="vContainer" style={{height: 500,background: 'yellow', overflow: 'scroll'}}>{value}</div>
}

ReactDOM.render(<App />, document.getElementById('mydiv'))
<script crossorigin src="https://unpkg.com/react@17/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
<body>
<div id="mydiv"></div>
</body>
Ajeet Shah
  • 18,551
  • 8
  • 57
  • 87
  • Well.. Thanks Ajeet. I tried your solution. But Log Value1 always 0 and Log Value2 increased value. 1, 2,3 ... – daniels Feb 25 '21 at 19:54
  • @daniels Yes. I kept that to see the difference. The `Log Value1` will always be `0`. – Ajeet Shah Feb 25 '21 at 19:55
  • Why is it always 0 instead of increased value? We have called ```setValue``` function. – daniels Feb 25 '21 at 20:10
  • Because the initial value is getting binded in the closure. More on closure: [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures), [W3Schools](https://www.w3schools.com/js/js_function_closures.asp), and [this SO post](https://stackoverflow.com/q/111102/2873538) – Ajeet Shah Feb 25 '21 at 20:14
  • Well @Ajeet, then how can I get the updated value in ```trackScrolling``` function? – daniels Feb 26 '21 at 00:13
  • You can get updated value if you use the value which is printed in this example as `Log Value2`. – Ajeet Shah Feb 26 '21 at 01:59
  • @daniels Apart from this answer, I have written another answer: https://stackoverflow.com/a/66435915/2873538 about another but **similar** issue. In that question, you will find few other answers which solves the issue using `useRef`, if your requirement can be fulfilled with a `ref`, go for the `ref`. Also, see https://github.com/facebook/react/issues/14010 – Ajeet Shah Mar 02 '21 at 08:50
  • 1
    thanks. useRef is what I was looking for. +1 – daniels Mar 12 '21 at 13:30
-2

Your console statement happens before you set the new value, just switch the two lines into:

 setValue(value + 1);
 console.log('Log Value: ', value);

silicakes
  • 6,364
  • 3
  • 28
  • 39