簡體   English   中英

React.useEffect 掛鈎並訂閱鼠標滾輪事件 - 正確的方法

[英]React.useEffect hook and subscribing to a mousewheel event - the right approach

目標:

  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 中被updateScroll並保持為 0。

linter 通知我應該包含 state.scrollTop 作為效果的依賴項,但我不希望這樣,因為它應該在組件安裝時只執行一次。

跟隨 linter 會導致無限循環。

或者,將updateScroll訂閱功能之外,然后根據updateScroll建議,將updateScroll添加到依賴項,並且(再次跟隨updateScrolluseCallback(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 ]);

題:

如何實現目標?

您可以將函數傳遞給setState而不是關閉useEffect處理程序中的state變量。

該函數的參數是當前狀態,您需要返回新狀態。 這樣useEffect不應該抱怨,因為你的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);
}
}, []);

有幾個問題:

  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)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM