Skip to content

Commit 5cca1ec

Browse files
committed
fix(useMediaQuery): Return boolean describing media query match on first render instead of undefined
This gets rid of the value changing from undefined to boolean on the first renders, which might trigger undesired side-effects for users. BREAKING CHANGE: On first render, boolean is returned instead of undefined. fix #1000
1 parent 25b7b33 commit 5cca1ec

File tree

3 files changed

+35
-26
lines changed

3 files changed

+35
-26
lines changed

src/useMediaQuery/__docs__/story.mdx

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import { Canvas, Meta, Story } from '@storybook/addon-docs';
2-
import { Example } from './example.stories';
3-
import { ImportPath } from '../../__docs__/ImportPath';
1+
import {Canvas, Meta, Story} from '@storybook/addon-docs';
2+
import {Example} from './example.stories';
3+
import {ImportPath} from '../../__docs__/ImportPath';
44

55
<Meta title="Sensor/useMediaQuery" component={Example} />
66

@@ -36,4 +36,3 @@ export function useMediaQuery(query: string): boolean | undefined;
3636
#### Return
3737

3838
`boolean` - `true` in case document matches media query, `false` otherwise.
39-
`undefined` - initially, when media query isn't matched yet.

src/useMediaQuery/__tests__/dom.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,9 @@ describe('useMediaQuery', () => {
5454
expect(result.error).toBeUndefined();
5555
});
5656

57-
it('should return undefined on first render', () => {
57+
it('should return value on first render', () => {
5858
const { result } = renderHook(() => useMediaQuery('max-width : 768px'));
59-
expect(result.all.length).toBe(2);
60-
expect(result.all[0]).toBe(undefined);
59+
expect(result.all.length).toBe(1);
6160
expect(result.current).toBe(false);
6261
});
6362

src/useMediaQuery/useMediaQuery.ts

Lines changed: 30 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { Dispatch, useEffect } from 'react';
2-
import { useSafeState } from '../useSafeState/useSafeState';
1+
import { Dispatch, useEffect, useState } from 'react';
2+
import { isBrowser } from '../util/const';
33

44
const queriesMap = new Map<
55
string,
@@ -8,24 +8,28 @@ const queriesMap = new Map<
88

99
type QueryStateSetter = (matches: boolean) => void;
1010

11+
const createQueryEntry = (query: string) => {
12+
const mql = matchMedia(query);
13+
const dispatchers = new Set<QueryStateSetter>();
14+
const listener = () => {
15+
dispatchers.forEach((d) => d(mql.matches));
16+
};
17+
18+
if (mql.addEventListener) mql.addEventListener('change', listener, { passive: true });
19+
else mql.addListener(listener);
20+
21+
return {
22+
mql,
23+
dispatchers,
24+
listener,
25+
};
26+
};
27+
1128
const querySubscribe = (query: string, setState: QueryStateSetter) => {
1229
let entry = queriesMap.get(query);
1330

1431
if (!entry) {
15-
const mql = matchMedia(query);
16-
const dispatchers = new Set<QueryStateSetter>();
17-
const listener = () => {
18-
dispatchers.forEach((d) => d(mql.matches));
19-
};
20-
21-
if (mql.addEventListener) mql.addEventListener('change', listener, { passive: true });
22-
else mql.addListener(listener);
23-
24-
entry = {
25-
mql,
26-
dispatchers,
27-
listener,
28-
};
32+
entry = createQueryEntry(query);
2933
queriesMap.set(query, entry);
3034
}
3135

@@ -54,12 +58,19 @@ const queryUnsubscribe = (query: string, setState: QueryStateSetter): void => {
5458
/**
5559
* Tracks the state of CSS media query.
5660
*
57-
* Return undefined initially and later receives correct value.
58-
*
5961
* @param query CSS media query to track.
6062
*/
6163
export function useMediaQuery(query: string): boolean | undefined {
62-
const [state, setState] = useSafeState<boolean>();
64+
const [state, setState] = useState<boolean | undefined>(() => {
65+
if (isBrowser) {
66+
let entry = queriesMap.get(query);
67+
if (!entry) {
68+
entry = createQueryEntry(query);
69+
queriesMap.set(query, entry);
70+
}
71+
return entry.mql.matches;
72+
}
73+
});
6374

6475
useEffect(() => {
6576
querySubscribe(query, setState);

0 commit comments

Comments
 (0)