React Hook-仅监听窗口*宽度*大小变化 [英] React Hook - Only listen to window *width* size change
问题描述
我有一个监听 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屋!