🤔️怎么做?实现一个优雅的监听屏幕宽度的hook
监听屏幕宽度,我们项目组之前通常解决方法是window.onresize方法。
const callback = (e)=>{
console.log('screen width has changed',e.target.window.innerWidth);
}
window.addEventListener('resize',callback);
window.matchMedia
这里介绍一个新的监听屏幕宽度的方法,性能更高
const mediaQuery = window.matchMedia("(min-width: 720px)");
console.log("is large 720px: ", mediaQuery.matches);
直接使用matchMedia
的API,就可以判断当前的屏幕宽度是否大于720px
。传入的参数和css媒体查询一致,也就意味着matchMedia
不仅仅可以查询屏幕宽度,屏幕是否倾斜,屏幕的像素,还有屏幕是否支持指针等等仅仅判断当前屏幕尺寸还是不够的,我们还需要知道屏幕尺寸什么变化为此,matchMedia
提供了增加屏幕监听函数的功能
const listener = (mediaQuery)=>{
console.log("is large 720px: ", mediaQuery.matches);
}
mediaQuery.addListener(listener);
效果:可以看到只有屏幕尺寸变化到720px前后,监听事件才会触发,性能嘎嘎好在项目中,当前组件卸载后,还可以将该监听事件卸载
mediaQuery.removeListener(listener);
好,使用方法就这些,很简单,就两个API:addListener
,removeListener
编写hook
设计思路:调用者传入监听的断点,然后hook返回布尔值,并且当屏幕变化至断点前后,布尔值的变化也会反应出来
//useMediaQuery.tsx
import {useEffect, useState} from 'react';
const breakPoints = {
mobile: "(min-width: 480px)",
ipad: "(min-width: 720px)",
pc: "(min-width: 1080px)"
}
const useMediaQuery = (breakPointName: string)=>{
const [matches, setMatches] = useState(false);
useEffect(()=>{
//if the breakPoint is error, then do nothing
if(!breakPoints[breakPointName]) return;
const breakPoint = breakPoints[breakPointName];
const mediaQuery = window.matchMedia(breakPoint);
setMatches(mediaQuery.matches);
const handler = (e: any)=> setMatches(e.matches);
mediaQuery.addListener(handler);
return ()=>{
mediaQuer.removeListener(handler);
}
},[]);
return matches;
}
代码很简单,传入断点,返回对这个断点的媒体查询。并且会在组件卸载的时候,对监听事件的移除用法:
const isIpad = useMediaQuery('ipad');
console.log('isIpad: ',isIpad);
如果需要同时监听多个断点,就需要调用多次useMediaQuery,像这样
const isIpad = useMediaQuery('ipad');
const isMobile = useMediaQuery('mobile');
const isPC = useMediaQuery('pc');
改进
下面对这个hook做些改进,同时可以监听多个媒体查询
type keyBreakPoints = keyof typeof breakPoints;
const useMultipleMediaQuery =
(breakPointNames: keyBreakPoints[]): Record<keyBreakPoints, boolean>=>{
const [matches, setMatches] = useState(breakPointNames.reduce((res, nextItem)=>{
res[nextItem] = false;
return res;
},{}));
const setBreakPointsMatchs = (breakPointName: keyBreakPoints, isMatch: boolean)=>{
matches[breakPointName] = isMatch;
setMatches({...matches});
}
useEffect(()=>{
try{
const unSubScribes = breakPointNames.reduce((res, breakPointName)=>{
const breakPoint = breakPoints[breakPointName];
const mediaQuery = window.matchMedia(breakPoint);
setBreakPointsMatchs(breakPointName, mediaQuery.matches);
const handler =
(e: any)=> setBreakPointsMatchs(breakPointName, mediaQuery.matches);
mediaQuery.addListener(handler);
res.push(()=>medaiQuery.removeListener(handler));
return res;
},[]);
return ()=>{
unSubScribes.forEach(callback=>{
callback && callback();
})
}
}catch{
return;
}
},[])
return matches;
}
useMultipleMediaQuery
支持了同时对多个断点进行媒体查询,通过传入一个断点的数组,并且返回的是一个Record<keyBreakPoints, boolean>类型的对象, 并且也会在组件卸载的时候,对所有的监听事件移除用法:
const { mobile, ipad, pc } = useMultipleMediaQuery(["mobile","ipad","pc"]);
console.log("is mobile: ", mobile);
console.log("is ipad: ", ipad);
console.log("is pc: ", pc);
总结:
这篇文文章介绍了matchMedia的用法,以及使用这个用法写了一个监听屏幕宽度的react hook,代码都是测试过的,可以直接拿来用。觉得有收获的小盆友可以点个赞哦✌️
转载自:https://juejin.cn/post/7267441882397147175