React Hook - Only listen to window *width* size change

懵懂的女人 提交于 2021-02-08 11:43:24

问题


I have a hook that listens to the window.resize event. I wish to only listen to and update when window.innerWidth changes. I wish to ignore changes to window.innerHeight since this gets triggered when opening the soft keyboard. The problem is that the mediaSize is stored in my Redux Store causing the app to re-render when the soft keyboard opens.

My code triggers on both window.innerWidth and window.innerHeight changes, how can I change this behaviour?

My hook

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
}

Implementation in my 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)

My mount point for AppRouter

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

Kind regards /K


回答1:


A simple solution I can think of, is to cache the last innerWidth and only perform your resize logic if it changed. Something like this:

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
}


来源:https://stackoverflow.com/questions/64411993/react-hook-only-listen-to-window-width-size-change

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!