React Hook-仅监听窗口*宽度*大小变化

时间:2020-10-18 09:59:08

标签: javascript reactjs react-hooks dom-events

我有一个监听window.resize事件的钩子。 我希望仅在window.innerWidth更改时收听和更新。我希望忽略对window.innerHeight的更改,因为打开软键盘时会触发此更改。 问题是mediaSize存储在我的Redux Store中,导致在打开软键盘时重新渲染应用程序。

我的代码在window.innerWidthwindow.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 个答案:

答案 0 :(得分:1)

我能想到的一个简单解决方案是缓存最后的innerWidth,并且仅在更改大小后才执行调整大小逻辑。像这样:

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
}