React Hook-仅监听窗口*宽度*大小变化 [英] React Hook - Only listen to window *width* size change

查看:1007
本文介绍了React Hook-仅监听窗口*宽度*大小变化的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

我的代码同时在 window.innerWidth window.innerHeight 上触发,如何更改此行为?

我的钩子

  import {useState,useEffect}从'react';//钩export const useWindowSize =()=>{const isClient = typeof window ==='object';//对象代表浏览器窗口函数getSize(){返回 {宽度:isClient?window.innerWidth:未定义}}const [windowSize,setWindowSize] = useState(getSize)useEffect(()=> {if(!isClient){return false}//如果不是用户/浏览器则退出函数handleResize(){setWindowSize(getSize())}window.addEventListener('resize',handleResize)//<-我只对window.innerWidth感兴趣!return()=>window.removeEventListener('resize',handleResize)},[])//空数组可确保仅在安装和卸载时运行效果返回windowSize} 

我的 AppRouter.js

中的实现

  const AppRouter =({isNavOpen,mediaSize,startSetMediaSize,language,... rest})=>{//**启动mediaQuery */const mediaBreakpoints = {smallStr:styles ['breakpoint-small-value'],mediumStr:styles ['breakpoint-medium-value'],largeStr:styles ['breakpoint-large-value'],smallInt:新Number(styles ['breakpoint-small-value']).valueOf(),mediumInt:新Number(styles ['breakpoint-medium-value']).valueOf(),largeInt:新Number(styles ['breakpoint-large-value']).valueOf(),小:styles ['breakpoint-small'],媒介:styles ['breakpoint-medium'],大:样式['breakpoint-large']}//计算媒体大小const screenWidth = useWindowSize().width...useEffect(()=> {如果(screenWidth> mediaBreakpoints.largeInt){如果(mediaSize!== MEDIA_LARGE){startSetMediaSize(MEDIA_LARGE)}}否则,如果(screenWidth> mediaBreakpoints.mediumInt){如果(mediaSize!== MEDIA_MEDIUM){startSetMediaSize(MEDIA_MEDIUM)}}否则,如果(screenWidth> mediaBreakpoints.smallInt){如果(mediaSize!== MEDIA_SMALL){startSetMediaSize(MEDIA_SMALL)}} 别的 {//小和小被同等对待如果(mediaSize!== MEDIA_SMALL){startSetMediaSize(MEDIA_SMALL)}}},[screenWidth])//仅在屏幕宽度更改时运行}const mapStateToProps =(状态)=>({isNavOpen:state.ui.isOpen,mediaSize:state.ui.mediaSize,//小/中/大语言:state.usersettings.language//se || en})const mapDispatchToProps =(dispatch)=>({startSetNavigationState:(isOpen)=>dispatch(setNavigationState(isOpen)),startSetMediaSize:(mediaSize)=>dispatch(setMediaSize(mediaSize))})//导出默认的AppRouter导出默认的connect(mapStateToProps,mapDispatchToProps)(AppRouter) 

我的 AppRouter

挂载点

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

亲切的问候/K

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

  import'react'中的{useState,useEffect,useRef};//钩export const useWindowSize =()=>{const isClient = typeof window ==='object';//对象代表浏览器窗口const lastWidth = useRef();函数getSize(){返回 {宽度:isClient?window.innerWidth:未定义}}const [windowSize,setWindowSize] = useState(getSize)useEffect(()=> {if(!isClient){return false}//如果不是用户/浏览器则退出函数handleResize(){如果(window?.innerWidth!== lastWidth.current){const width = getSize();lastWidth.current =宽度;setWindowSize(width)}}window.addEventListener('resize',handleResize)//<-我只对window.innerWidth感兴趣!return()=>window.removeEventListener('resize',handleResize)},[])//空数组可确保仅在安装和卸载时运行效果返回windowSize} 

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

解决方案

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
}

这篇关于React Hook-仅监听窗口*宽度*大小变化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆