几天后,我采用了以下方法,以声明式和命令式的方式从 JS 处理断点更改。
基于react-responsive包,我实现了一个小型库,它提供断点上下文和钩子(以声明方式处理当前断点)并发布断点更改通知(以命令式处理断点更改)。
如果您有改进的建议,请告诉我。
//
// breakpoints.tsx
//
import { default as React, ReactNode } from 'react'
import { useMediaQuery } from 'react-responsive'
export enum Breakpoint {
XS = 0,
SM = 640,
MD = 768,
LG = 1024,
XL = 1280,
XXL = 1536,
}
const allBreakpoints = Object.values(Breakpoint)
.filter((v) => typeof v === 'number')
.sort((a, b) => (a as number) - (b as number)) as Breakpoint[]
function activeBreakpoint(matches: boolean[]): Breakpoint {
const index = matches.lastIndexOf(true)
return allBreakpoints[index]
}
export type BreakpointEvent = CustomEvent<{ breakpoint: Breakpoint }>
function postBreakpointChangeEvent(breakpoint: Breakpoint): void {
window.dispatchEvent(
new CustomEvent('breakpointChange', {
detail: {
breakpoint,
},
})
)
}
const breakpointContext = React.createContext<Breakpoint>(Breakpoint.XS)
export function BreakpointProvider({ children }: { children: ReactNode }): JSX.Element {
const matches = allBreakpoints.map((bp: number, index: number) => {
// eslint-disable-next-line react-hooks/rules-of-hooks
return useMediaQuery({ query: `(min-width: ${bp}px)` }, undefined, (macthes: boolean) => {
const breakpoint = activeBreakpoint(matches)
postBreakpointChangeEvent(breakpoint)
})
})
const breakpoint = activeBreakpoint(matches)
return <breakpointContext.Provider value={breakpoint}>{children}</breakpointContext.Provider>
}
export function useBreakpoint(): Breakpoint {
return React.useContext(breakpointContext)
}
//
// index.tsx
//
ReactDOM.render(
<React.StrictMode>
<BreakpointProvider>
<App />
</BreakpointProvider>
</React.StrictMode>,
document.getElementById('root')
)
//
// handle breakpoint changes imperatively
//
const handleBreakpointChange = (evt: Event) => {
if ((evt as BreakpointEvent).detail.breakpoint >= Breakpoint.MD) {
setMenuShowing(false)
}
}
React.useEffect(() => {
window.addEventListener('breakpointChange', handleBreakpointChange)
return () => window.removeEventListener('breakpointChange', handleBreakpointChange)
}, [])
//
// handle breakpoint changes declaratively.
// re-renders each time the breakpoint changes
//
const breakpoint = useBreakpoint()
if (breakpoint >= Breakpoint.MD) {
// render for medium and larger screens
} else {
// render for less than smaller screeens
}