title: React.useEffect hook and subscribing to a mousewheel event - the right approach
body: <p><strong>The goal:</strong></p>
  
  <ol>
  <li><p>Subscribe to the mousewheel <code>event</code> once, at component mount.</p></li>
  <li><p>Update the state.scrollTop of the component with <code>newValue</code>, where:</p></li>
  </ol>
  
  <p><code>newValue = Math.max(0, state.scrollTop + event.deltaY)</code></p>
  
  <ol start="3">
  <li>Keep react hooks linter happy without disabling default rules.</li>
  </ol>
  
  <p><strong>The code:</strong></p>
  
  <pre><code>  const [ state, setState ] = useState({ scrollTop: 0 });
  
    useEffect(function subscribeToWheelEvent() {
      const updateScroll = function(e) {
        if(!!e.deltaY) {
          const delta = Math.sign(e.deltaY) * 10.0;
          const val = Math.max(0, state.scrollTop + delta);
          console.log(delta, val);
          setState({ scrollTop: val })
        } else {
          console.log('zero', e.deltaY);
        }
      }
      window.addEventListener('mousewheel', updateScroll);
      console.log('subscribed to wheelEvent')
      return function () {
        window.removeEventListener('mousewheel', updateScroll);
      }
    }, []);
  
  </code></pre>
  
  <p><strong>Expected result:</strong></p>
  
  <p>The <code>state.scrollTop</code> value updates every mouse wheel event by +-10 depending on scroll direction and is capped at 0 if <code>newValue</code> is smaller than 0.</p>
  
  <p><strong>Current result:</strong></p>
  
  <p>The <code>state.scrollTop</code> value does not update properly, switches between 0 and 10, probably because it is memoized inside the <code>updateScroll</code> and remains at 0.</p>
  
  <p>The linter informs me that I should include state.scrollTop as dependency of the effect but I don't want that as it should be executing only once at component mount. </p>
  
  <p>Following the linter causes infinite loop.</p>
  
  <p>Alternatively, moving <code>updateScroll</code> outside of subscription function, then as per linter suggestions, adding <code>updateScroll</code> to dependencies, and (again following the linter) wrapping <code>updateScroll</code> with <code>useCallback(updateScroll, [state.scrollTop] )</code> results in subscribing/unsubscribing to the wheel event on every <code>scrollTop</code> change.</p>
  
  <p><strong>Alternative code:</strong></p>
  
  <pre><code>  const [ state, setState ] = useState({ scrollTop: 0 });
  
    const updateScroll = useCallback(function(e) {
      if(!!e.deltaY) {
        const delta = Math.sign(e.deltaY) * 10.0;
        const val = Math.max(0, state.scrollTop + delta);
        console.log(delta, val );
        setState({ scrollTop: val })
      } else {
        console.log('zero', e.deltaY);
      }
    }, [ state.scrollTop ])
  
    useEffect(function subscribeToWheelEvent() {
      window.addEventListener('mousewheel', updateScroll);
      console.log('subscribed to wheelEvent')
      return function () {
        window.removeEventListener('mousewheel', updateScroll);
        console.log('unsubscribed to wheelEvent')
      }
    }, [ updateScroll ]);
  </code></pre>
  
  <p><strong>Question:</strong> </p>
  
  <p>How to achieve the goal?</p>