Реагирующий хук — только прослушивание окна * изменение ширины * размера

#javascript #reactjs #react-hooks #dom-events

#javascript #reactjs #реагирующие перехваты #dom-события

Вопрос:

У меня есть хук, который прослушивает window.resize событие. Я хочу только прослушивать и обновлять при window.innerWidth изменениях. Я хочу игнорировать изменения window.innerHeight , поскольку это срабатывает при открытии программной клавиатуры. Проблема в том, что размер среды хранится в моем хранилище Redux, что приводит к повторному отображению приложения при открытии программной клавиатуры.

Мой код срабатывает на обоих window.innerWidth и window.innerHeight изменяется, как я могу изменить это поведение?

Мой хук

 import { useState, useEffect } from 'react';

// Hook
export const useWindowSize = () => {
  const isClient = typeof window === 'object'; //Object represents browser window
  function getSize() {
    return {
      width: isClient ? window.innerWidth : undefined
    }
  }

  const [windowSize, setWindowSize] = useState(getSize)

  useEffect(() => {
    if (!isClient) { return false } //Exit if not user/browser

    function handleResize() {
      setWindowSize(getSize())
    }
    window.addEventListener('resize', handleResize) // <-- I am only interested in window.innerWidth !
    return () => window.removeEventListener('resize', handleResize)
  }, []) // Empty array ensures that effect is only run on mount and unmount

  return windowSize
}
  

Реализация в моем AppRouter.js

 const AppRouter = ({ isNavOpen, mediaSize, startSetMediaSize, language, ...rest }) => {

  //** Start mediaQuery */
  const mediaBreakpoints = {
    smallStr: styles['breakpoint-small-value'],
    mediumStr: styles['breakpoint-medium-value'],
    largeStr: styles['breakpoint-large-value'],
    smallInt: new Number(styles['breakpoint-small-value']).valueOf(),
    mediumInt: new Number(styles['breakpoint-medium-value']).valueOf(),
    largeInt: new Number(styles['breakpoint-large-value']).valueOf(),
    small: styles['breakpoint-small'],
    medium: styles['breakpoint-medium'],
    large: styles['breakpoint-large']
  }

  //Calculate media size
  const screenWidth = useWindowSize().width
  ...
  useEffect(() => {
    if (screenWidth > mediaBreakpoints.largeInt) {
      if (mediaSize !== MEDIA_LARGE) { startSetMediaSize(MEDIA_LARGE) }
    } else if (screenWidth > mediaBreakpoints.mediumInt) { 
      if (mediaSize !== MEDIA_MEDIUM) { startSetMediaSize(MEDIA_MEDIUM) }
    } else if (screenWidth > mediaBreakpoints.smallInt) {
      if (mediaSize !== MEDIA_SMALL) { startSetMediaSize(MEDIA_SMALL) }
    } else { 
      //Tiny and small are treated equally
      if (mediaSize !== MEDIA_SMALL) { startSetMediaSize(MEDIA_SMALL) }
    } 
  }, [screenWidth]) // Only run if screen width changes
}

const mapStateToProps = (state) => ({
  isNavOpen : state.ui.isOpen,
  mediaSize: state.ui.mediaSize, //small / medium / large
  language: state.usersettings.language //se||en
})

const mapDispatchToProps = (dispatch) => ({
  startSetNavigationState: (isOpen) => dispatch(setNavigationState(isOpen)),
  startSetMediaSize: (mediaSize) => dispatch(setMediaSize(mediaSize))
})

//export default AppRouter
export default connect(mapStateToProps, mapDispatchToProps)(AppRouter)
  

Моя точка монтирования для AppRouter

 const jsx = (
    <React.StrictMode>
      <Provider store={store}>
        <AppRouter />
      </Provider>
    </React.StrictMode>
)
  

С уважением / K

Ответ №1:

Простое решение, которое я могу придумать, — это кэшировать последнюю внутреннюю ширину и выполнять логику изменения размера только в том случае, если она изменилась. Что-то вроде этого:

 import { useState, useEffect, useRef } from 'react';

// Hook
export const useWindowSize = () => {
  const isClient = typeof window === 'object'; //Object represents browser window
  const lastWidth = useRef();

  function getSize() {
    return {
      width: isClient ? window.innerWidth : undefined
    }
  }

  const [windowSize, setWindowSize] = useState(getSize)

  useEffect(() => {
    if (!isClient) { return false } //Exit if not user/browser

    function handleResize() {
      if (window?.innerWidth !== lastWidth.current) {
        const width = getSize();
        lastWidth.current = width;
        setWindowSize(width)
      }
    }
    window.addEventListener('resize', handleResize) // <-- I am only interested in window.innerWidth !
    return () => window.removeEventListener('resize', handleResize)
  }, []) // Empty array ensures that effect is only run on mount and unmount

  return windowSize
}
  

Комментарии:

1. Большое спасибо @Sagi! Это сработало как шарм! (^ __ ^)/ Я раньше не использовал хук useRef, прочитаю об этом! С уважением / K