Skip to content

Commit 0af97f1

Browse files
committed
feat(useMediaQuery): Add initializeWithValue option
This allows us to give the users the media query match state immediately on first render and optionally give them SSR compatibility. BREAKING CHANGE: Returned value is no longer undefined on first render. Set initializeWithValue to false to re-enable this behavior. fix #1000
1 parent 5cca1ec commit 0af97f1

File tree

5 files changed

+41
-11
lines changed

5 files changed

+41
-11
lines changed

src/useMediaQuery/__docs__/story.mdx

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,11 @@ Tracks the state of CSS media query.
2222
## Reference
2323

2424
```ts
25-
export function useMediaQuery(query: string): boolean | undefined;
25+
interface UseMediaQueryOptions {
26+
initializeWithValue?: boolean;
27+
}
28+
29+
export function useMediaQuery(query: string, options?: UseMediaQueryOptions): boolean | undefined;
2630
```
2731

2832
#### Importing
@@ -32,7 +36,12 @@ export function useMediaQuery(query: string): boolean | undefined;
3236
#### Arguments
3337

3438
- **query** _`string`_ - CSS media query to track.
39+
- **options** _`object`_ - Hook options:
40+
- **initializeWithValue** _`boolean`_ _(default: true)_ - Determine media query match state on first
41+
render. Setting this to false will make the hook yield `undefined` on first render. We suggest
42+
setting this to `false` during SSR.
3543

3644
#### Return
3745

38-
`boolean` - `true` in case document matches media query, `false` otherwise.
46+
`boolean` - `true` if document matches the media query, `false` if not. `undefined` on first render
47+
if `initializeWithValue` was set to `false`.

src/useMediaQuery/__tests__/dom.ts

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

57-
it('should return value on first render', () => {
58-
const { result } = renderHook(() => useMediaQuery('max-width : 768px'));
57+
it('should return undefined on first render, if initializeWithValue is false', () => {
58+
const { result } = renderHook(() =>
59+
useMediaQuery('max-width : 768px', { initializeWithValue: false })
60+
);
61+
expect(result.all.length).toBe(2);
62+
expect(result.all[0]).toBe(undefined);
63+
expect(result.current).toBe(false);
64+
});
65+
66+
it('should return value on first render, if initializeWithValue is true', () => {
67+
const { result } = renderHook(() =>
68+
useMediaQuery('max-width : 768px', { initializeWithValue: true })
69+
);
5970
expect(result.all.length).toBe(1);
6071
expect(result.current).toBe(false);
6172
});

src/useMediaQuery/__tests__/ssr.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,16 @@ describe('useMediaQuery', () => {
77
});
88

99
it('should render', () => {
10-
const { result } = renderHook(() => useMediaQuery('max-width : 768px'));
10+
const { result } = renderHook(() =>
11+
useMediaQuery('max-width : 768px', { initializeWithValue: false })
12+
);
1113
expect(result.error).toBeUndefined();
1214
});
1315

14-
it('should return undefined on first render', () => {
15-
const { result } = renderHook(() => useMediaQuery('max-width : 768px'));
16+
it('should return undefined on first render, if initializeWithValue is set to false', () => {
17+
const { result } = renderHook(() =>
18+
useMediaQuery('max-width : 768px', { initializeWithValue: false })
19+
);
1620
expect(result.current).toBeUndefined();
1721
});
1822
});

src/useMediaQuery/useMediaQuery.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { Dispatch, useEffect, useState } from 'react';
2-
import { isBrowser } from '../util/const';
32

43
const queriesMap = new Map<
54
string,
@@ -55,14 +54,21 @@ const queryUnsubscribe = (query: string, setState: QueryStateSetter): void => {
5554
}
5655
};
5756

57+
interface UseMediaQueryOptions {
58+
initializeWithValue?: boolean;
59+
}
60+
5861
/**
5962
* Tracks the state of CSS media query.
6063
*
6164
* @param query CSS media query to track.
65+
* @param options Hook options:
66+
* `initializeWithValue` (default: `true`) - Determine media query match state on first render. Setting
67+
* this to false will make the hook yield `undefined` on first render.
6268
*/
63-
export function useMediaQuery(query: string): boolean | undefined {
69+
export function useMediaQuery(query: string, options?: UseMediaQueryOptions): boolean | undefined {
6470
const [state, setState] = useState<boolean | undefined>(() => {
65-
if (isBrowser) {
71+
if (options?.initializeWithValue ?? true) {
6672
let entry = queriesMap.get(query);
6773
if (!entry) {
6874
entry = createQueryEntry(query);

src/useScreenOrientation/useScreenOrientation.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ export type ScreenOrientation = 'portrait' | 'landscape';
99
* hook uses CSS3 `orientation` media-query to check screen orientation.
1010
*/
1111
export function useScreenOrientation(): ScreenOrientation | undefined {
12-
const matches = useMediaQuery('(orientation: portrait)');
12+
const matches = useMediaQuery('(orientation: portrait)', { initializeWithValue: false });
1313

1414
return typeof matches === 'undefined' ? undefined : matches ? 'portrait' : 'landscape';
1515
}

0 commit comments

Comments
 (0)