/
use-breakpoint.ts
91 lines (79 loc) · 2.75 KB
/
use-breakpoint.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
import React from "react"
import { useEnvironment } from "@chakra-ui/react-env"
import { useTheme } from "@chakra-ui/system"
/**
* React hook used to get the current responsive media breakpoint.
*
* @param [defaultBreakpoint="base"] default breakpoint name
* (in non-window environments like SSR)
*
* For SSR, you can use a package like [is-mobile](https://github.com/kaimallea/isMobile)
* to get the default breakpoint value from the user-agent
*/
export function useBreakpoint(
defaultBreakpoint = "base", // default value ensures SSR+CSR consistency
) {
const { __breakpoints } = useTheme()
const env = useEnvironment()
const queries = React.useMemo(
() =>
__breakpoints?.details.map(({ minMaxQuery, breakpoint }) => ({
breakpoint,
query: minMaxQuery.replace("@media screen and ", ""),
})) ?? [],
[__breakpoints],
)
const [currentBreakpoint, setCurrentBreakpoint] = React.useState(() => {
if (defaultBreakpoint) {
// use default breakpoint to ensure render consistency in SSR + CSR environments
// => first render on the client has to match the render on the server
const fallbackBreakpointDetail = queries.find(
({ breakpoint }) => breakpoint === defaultBreakpoint,
)
if (fallbackBreakpointDetail) {
return fallbackBreakpointDetail.breakpoint
}
}
if (env.window.matchMedia) {
// set correct breakpoint on first render if no default breakpoint was provided
const matchingBreakpointDetail = queries.find(
({ query }) => env.window.matchMedia(query).matches,
)
if (matchingBreakpointDetail) {
return matchingBreakpointDetail.breakpoint
}
}
return undefined
})
React.useEffect(() => {
const allUnregisterFns = queries.map(({ breakpoint, query }) => {
const mediaQueryList = env.window.matchMedia(query)
if (mediaQueryList.matches) {
setCurrentBreakpoint(breakpoint)
}
const handleChange = (ev: MediaQueryListEvent) => {
if (ev.matches) {
setCurrentBreakpoint(breakpoint)
}
}
// add media query listener
if (typeof mediaQueryList.addEventListener === "function") {
mediaQueryList.addEventListener("change", handleChange)
} else {
mediaQueryList.addListener(handleChange)
}
// return unregister fn
return () => {
if (typeof mediaQueryList.removeEventListener === "function") {
mediaQueryList.removeEventListener("change", handleChange)
} else {
mediaQueryList.removeListener(handleChange)
}
}
})
return () => {
allUnregisterFns.forEach((unregister) => unregister())
}
}, [queries, __breakpoints, env.window])
return currentBreakpoint
}