useResponsive
作用
获取响应式信息
原理
是监听
resize
方法,判断与配置的每一种宽度,大于则为true
,否则为false
源码
ts
import { useEffect, useState } from 'react'
import isBrowser from '../utils/isBrowser'
type Subscriber = () => void
// 订阅者集合
const subscribers = new Set<Subscriber>()
type ResponsiveConfig = Record<string, number>
type ResponsiveInfo = Record<string, boolean>
let info: ResponsiveInfo
// 默认配置
let responsiveConfig: ResponsiveConfig = {
xs: 0,
sm: 576,
md: 768,
lg: 992,
xl: 1200,
}
// resize 事件回调
function handleResize() {
const oldInfo = info
calculate()
if (oldInfo === info) return
for (const subscriber of subscribers) {
subscriber()
}
}
// 是否监听 resize 事件的标识
let listening = false
// 计算当前响应式信息
function calculate() {
const width = window.innerWidth
const newInfo = {} as ResponsiveInfo
let shouldUpdate = false
for (const key of Object.keys(responsiveConfig)) {
newInfo[key] = width >= responsiveConfig[key]
if (newInfo[key] !== info[key]) {
shouldUpdate = true
}
}
if (shouldUpdate) {
info = newInfo
}
}
// 设置配置的方法
export function configResponsive(config: ResponsiveConfig) {
responsiveConfig = config
if (info) calculate()
}
export function useResponsive() {
// 判断是否为浏览器环境,并且没有监听 resize 事件
if (isBrowser && !listening) {
info = {}
calculate()
// 监听 resize 事件
window.addEventListener('resize', handleResize)
// 标识已经监听
listening = true
}
const [state, setState] = useState<ResponsiveInfo>(info)
useEffect(() => {
// 如果不是浏览器环境,则直接返回
if (!isBrowser) return
// 如果没有监听 resize 事件,则添加监听
// In React 18's StrictMode, useEffect perform twice, resize listener is remove, so handleResize is never perform.
// https://github.com/alibaba/hooks/issues/1910
if (!listening) {
window.addEventListener('resize', handleResize)
}
const subscriber = () => {
setState(info)
}
// 添加订阅者
subscribers.add(subscriber)
return () => {
// 移除订阅者
subscribers.delete(subscriber)
// 如果订阅者为空,则移除 resize 事件监听
if (subscribers.size === 0) {
window.removeEventListener('resize', handleResize)
// 标识重置为false,表示没有监听
listening = false
}
}
}, [])
return state
}