【问题标题】:React.useEffect hook and subscribing to a mousewheel event - the right approachReact.useEffect 钩子和订阅鼠标滚轮事件 - 正确的方法
【发布时间】:2020-04-27 02:10:45
【问题描述】:

目标:

  1. 在组件挂载时订阅鼠标滚轮event

  2. newValue更新组件的state.scrollTop,其中:

newValue = Math.max(0, state.scrollTop + event.deltaY)

  1. 在不禁用默认规则的情况下保持 react hooks linter 愉快。

代码:

  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);
    }
  }, []);

预期结果:

state.scrollTop 值根据滚动方向将每个鼠标滚轮事件更新 +-10,如果 newValue 小于 0,则上限为 0。

当前结果:

state.scrollTop 值没有正确更新,在 0 和 10 之间切换,可能是因为它在 updateScroll 中被记忆并保持为 0。

linter 通知我应该包含 state.scrollTop 作为效果的依赖项,但我不希望这样,因为它应该只在组件挂载时执行一次。

跟随 linter 会导致无限循环。

或者,将updateScroll 移到订阅功能之外,然后根据 linter 的建议,将 updateScroll 添加到依赖项中,并且(再次遵循 linter)用 useCallback(updateScroll, [state.scrollTop] ) 包装 updateScroll 导致订阅/取消订阅轮子每次 scrollTop 更改时发生的事件。

替代代码:

  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 ]);

问题:

如何实现目标?

【问题讨论】:

  • 存在一些边缘情况,其中 linter 没有首选选项。我认为对于你想要完成的事情,你需要使用你的第一种方法。

标签: javascript reactjs react-hooks create-react-app eslint


【解决方案1】:

您可以将函数传递给setState,而不是关闭useEffect 处理程序中的state 变量。

函数的参数是当前状态,需要返回新的状态。这样,linter 就不会抱怨,因为您的 useEffect 不依赖于状态变量。

类似:

const [ state, setState ] = useState({ scrollTop: 0 });

useEffect(function subscribeToWheelEvent() {
const updateScroll = function(e) {
  if(!!e.deltaY) {
    setState((currentState)=>{
         const delta = Math.sign(e.deltaY) * 10.0;
         const val = Math.max(0, currentState.scrollTop + delta);
         return {scrollTop:val}   
    })            
  }

  } else {
    console.log('zero', e.deltaY);
  }
}
window.addEventListener('mousewheel', updateScroll);
console.log('subscribed to wheelEvent')
return function () {
  window.removeEventListener('mousewheel', updateScroll);
}
}, []);

【讨论】:

    【解决方案2】:

    有几个问题:

    1. 我没有鼠标滚轮,但不推荐使用事件mousewheel - 您可能想使用其他东西。我订阅了scroll 事件。

    2. 我不确定您要尝试使用 delta 和 val 计算什么,但 const val = Math.max(0, state.scrollTop + delta) 对我来说似乎没有按预期工作。您的意思是计算 CURRENT scrollTop 值,而不是存储的值吗?无论哪种方式,这都是导致您的值返回为 10 的原因,也是导致重新渲染循环的原因。

    所以我认为这就是你所追求的:

    const [state, setState] = useState({ scrollTop: 0 })
    
    const updateScroll = useCallback(() => {
      if (window.pageYOffset) {
        const delta = Math.sign(window.pageYOffset) * 10.0
        const val = Math.max(0, window.pageYOffset + delta)
        setState({ scrollTop: val })
      } else {
        console.log('zero', window.pageYOffset)
      }
    }, [])
    
    useEffect(
      function subscribeToWheelEvent () {
        console.log('subscribed to wheelEvent')
        window.addEventListener('scroll', updateScroll)
        return function () {
          window.removeEventListener('scroll', updateScroll)
        }
      },
      [updateScroll]
    )
    
    console.log(state.scrollTop)
    

    【讨论】:

    • 感谢您的回答,我这里要处理鼠标滚轮。
    • 酷 - 其余部分同样适用。鼠标滚轮事件应该仍然有效:)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-12-30
    相关资源
    最近更新 更多