Tengo un gancho que escucha el evento window.resize. Solo deseo escuchar y actualizar cuando cambie window.innerWidth. Deseo ignorar los cambios en window.innerHeight ya que esto se activa al abrir el teclado virtual. El problema es que mediaSize está almacenado en mi tienda Redux, lo que hace que la aplicación vuelva a renderizarse cuando se abre el teclado virtual.

Mi código se activa en los cambios de window.innerWidth y window.innerHeight, ¿cómo puedo cambiar este comportamiento?

Mi gancho

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
}

Implementación en mi 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)

Mi punto de montaje para AppRouter

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

Saludos cordiales / K

0
Kermit 18 oct. 2020 a las 12:59

1 respuesta

La mejor respuesta

Una solución simple en la que puedo pensar es almacenar en caché el último innerWidth y solo realizar su lógica de cambio de tamaño si cambió. Algo como esto:

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
}
2
Sagi Rika 18 oct. 2020 a las 10:21