Skip to content

Commit a870e6b

Browse files
authored
fix: use sorted breakpoints in useBreakpoint (#5576)
Co-authored-by: Tim Kolberger <Tim.Kolberger@deutschebahn.com>
1 parent 44c9fab commit a870e6b

File tree

7 files changed

+68
-287
lines changed

7 files changed

+68
-287
lines changed

.changeset/fuzzy-zoos-kiss.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"@chakra-ui/media-query": patch
3+
---
4+
5+
Fixed an issue where the hook `useBreakpoint` did not work as expected with
6+
custom breakpoints

.changeset/large-fireants-check.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"@chakra-ui/utils": patch
3+
---
4+
5+
Fixed an issue where `queryString()` created invalid media queries when min and
6+
max were set.

packages/media-query/src/create-media-query.ts

Lines changed: 0 additions & 91 deletions
This file was deleted.
Lines changed: 43 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,6 @@
1+
import React from "react"
12
import { useEnvironment } from "@chakra-ui/react-env"
23
import { useTheme } from "@chakra-ui/system"
3-
import React from "react"
4-
import createMediaQueries from "./create-media-query"
5-
6-
interface Listener {
7-
mediaQuery: MediaQueryList
8-
handleChange: () => void
9-
}
10-
11-
export interface Breakpoint {
12-
breakpoint: string
13-
maxWidth?: string
14-
minWidth: string
15-
}
164

175
/**
186
* React hook used to get the current responsive media breakpoint.
@@ -24,89 +12,73 @@ export interface Breakpoint {
2412
* to get the default breakpoint value from the user-agent
2513
*/
2614
export function useBreakpoint(defaultBreakpoint?: string) {
27-
const { breakpoints } = useTheme()
15+
const { __breakpoints } = useTheme()
2816
const env = useEnvironment()
2917

30-
const mediaQueries = React.useMemo(
31-
() => createMediaQueries({ base: "0px", ...breakpoints }),
32-
[breakpoints],
18+
const queries = React.useMemo(
19+
() =>
20+
__breakpoints?.details.map(({ minMaxQuery, breakpoint }) => ({
21+
breakpoint,
22+
query: minMaxQuery.replace("@media screen and ", ""),
23+
})) ?? [],
24+
[__breakpoints],
3325
)
3426

3527
const [currentBreakpoint, setCurrentBreakpoint] = React.useState(() => {
3628
if (env.window.matchMedia) {
37-
let maxBreakpoint
38-
mediaQueries.forEach(({ query, ...breakpoint }) => {
39-
const mediaQuery = env.window.matchMedia(query)
40-
if (mediaQuery.matches) {
41-
maxBreakpoint = breakpoint
42-
}
43-
})
44-
if (maxBreakpoint) {
45-
return maxBreakpoint
46-
}
29+
// set correct breakpoint on first render
30+
const matchingBreakpointDetail = queries.find(
31+
({ query }) => env.window.matchMedia(query).matches,
32+
)
33+
return matchingBreakpointDetail?.breakpoint
4734
}
4835

49-
if (!defaultBreakpoint) {
50-
return undefined
51-
}
52-
53-
const mediaQuery = mediaQueries.find(
54-
({ breakpoint }) => breakpoint === defaultBreakpoint,
55-
)
56-
57-
if (mediaQuery) {
58-
const { query, ...breakpoint } = mediaQuery
59-
return breakpoint
36+
if (defaultBreakpoint) {
37+
// use fallback if available
38+
const fallbackBreakpointDetail = queries.find(
39+
({ breakpoint }) => breakpoint === defaultBreakpoint,
40+
)
41+
return fallbackBreakpointDetail?.breakpoint
6042
}
6143

6244
return undefined
6345
})
6446

65-
const current = currentBreakpoint?.breakpoint
47+
React.useEffect(() => {
48+
const allUnregisterFns = queries.map(({ breakpoint, query }) => {
49+
const mediaQueryList = env.window.matchMedia(query)
6650

67-
const update = React.useCallback(
68-
(query: MediaQueryList, breakpoint: Breakpoint) => {
69-
if (query.matches && current !== breakpoint.breakpoint) {
51+
if (mediaQueryList.matches) {
7052
setCurrentBreakpoint(breakpoint)
7153
}
72-
},
73-
[current],
74-
)
75-
76-
React.useEffect(() => {
77-
const listeners = new Set<Listener>()
78-
79-
mediaQueries.forEach(({ query, ...breakpoint }) => {
80-
const mediaQuery = env.window.matchMedia(query)
8154

82-
// trigger an initial update to determine media query
83-
update(mediaQuery, breakpoint)
84-
85-
const handleChange = () => {
86-
update(mediaQuery, breakpoint)
55+
const handleChange = (ev: MediaQueryListEvent) => {
56+
if (ev.matches) {
57+
setCurrentBreakpoint(breakpoint)
58+
}
8759
}
8860

89-
// add media query-listener
90-
mediaQuery.addListener(handleChange)
91-
92-
// push the media query list handleChange
93-
// so we can use it to remove Listener
94-
listeners.add({ mediaQuery, handleChange })
61+
// add media query listener
62+
if (typeof mediaQueryList.addEventListener === "function") {
63+
mediaQueryList.addEventListener("change", handleChange)
64+
} else {
65+
mediaQueryList.addListener(handleChange)
66+
}
9567

68+
// return unregister fn
9669
return () => {
97-
// clean up 1
98-
mediaQuery.removeListener(handleChange)
70+
if (typeof mediaQueryList.removeEventListener === "function") {
71+
mediaQueryList.removeEventListener("change", handleChange)
72+
} else {
73+
mediaQueryList.removeListener(handleChange)
74+
}
9975
}
10076
})
10177

10278
return () => {
103-
// clean up 2: for safety
104-
listeners.forEach(({ mediaQuery, handleChange }) => {
105-
mediaQuery.removeListener(handleChange)
106-
})
107-
listeners.clear()
79+
allUnregisterFns.forEach((unregister) => unregister())
10880
}
109-
}, [mediaQueries, breakpoints, update, env.window])
81+
}, [queries, __breakpoints, env.window])
11082

111-
return current
83+
return currentBreakpoint
11284
}

packages/media-query/tests/create-media-query.test.ts

Lines changed: 0 additions & 111 deletions
This file was deleted.

packages/utils/src/breakpoint.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,10 @@ function subtract(value: string) {
4444
}
4545

4646
function queryString(min: string | null, max?: string) {
47-
const query = []
47+
const query = ["@media screen"]
4848

49-
if (min) query.push(`@media screen and (min-width: ${px(min)})`)
50-
if (query.length > 0 && max) query.push("and")
51-
if (max) query.push(`@media screen and (max-width: ${px(max)})`)
49+
if (min) query.push("and", `(min-width: ${px(min)})`)
50+
if (max) query.push("and", `(max-width: ${px(max)})`)
5251

5352
return query.join(" ")
5453
}

0 commit comments

Comments
 (0)