Перехват React.useEffect и подписка на событие mousewheel - правильный подход - PullRequest
1 голос
/ 09 января 2020

Цель:

  1. Подписаться на колесо мыши event один раз при монтировании компонента.

  2. Обновление state.scrollTop компонента с newValue, где:

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

Держите ловушки реагирования счастливыми без отключения правил по умолчанию.

Код:

  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 в зависимости от направления прокрутки и ограничивается 0, если newValue меньше 0.

Текущий результат:

Значение state.scrollTop не обновляется должным образом, переключается между 0 и 10, возможно, потому что оно запоминается внутри updateScroll и остается на 0.

Линтер сообщает мне, что Я должен включить state.scrollTop в качестве зависимости эффекта, но я не хочу этого, так как он должен выполняться только один раз при монтировании компонента.

Следование за линтером приводит к бесконечности l oop.

В качестве альтернативы, перемещение updateScroll за пределы функции подписки, затем в соответствии с предложениями линтера, добавление updateScroll к зависимостям и (снова после обертывание линтера updateScroll с useCallback(updateScroll, [state.scrollTop] ) приводит к подписке / отмене подписки на событие колеса при каждом изменении 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 ]);

Вопрос:

Как достичь цели?

Ответы [ 2 ]

1 голос
/ 09 января 2020

Вместо закрытия переменной state в вашем обработчике useEffect вы можете передать функцию в setState.

Параметром функции является текущее состояние, и вам нужно вернуть новое состояние. Таким образом, линтер не должен жаловаться, так как 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);
}
}, []);
0 голосов
/ 09 января 2020

Было несколько проблем:

  1. У меня нет колесика мыши, но событие mousewheel устарело - возможно, вы захотите использовать что-то еще. Я подписался на событие scroll.

  2. Я не совсем уверен, что вы пытаетесь вычислить с помощью delta и val, но const val = Math.max(0, state.scrollTop + delta), похоже, не работает как ожидается для меня. Вы имели в виду вычислять значение CURRENT scrollTop, а не сохраненное? В любом случае, именно это и привело к тому, что ваше значение вернулось как 10, и именно это стало причиной повторного рендеринга l oop.

Так что я думаю, что это то, что вы ' после:

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)
...