Skip to content

Commit

Permalink
feat: add useMediaQuery
Browse files Browse the repository at this point in the history
  • Loading branch information
mannix-lei committed Jan 29, 2024
1 parent 5752f30 commit efe0dba
Show file tree
Hide file tree
Showing 7 changed files with 152 additions and 0 deletions.
1 change: 1 addition & 0 deletions config/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ export const menus = [
'useScroll',
'useSize',
'useFocusWithin',
'useMediaQuery',
],
},
{
Expand Down
2 changes: 2 additions & 0 deletions packages/hooks/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ import useVirtualList from './useVirtualList';
import useWebSocket from './useWebSocket';
import useWhyDidYouUpdate from './useWhyDidYouUpdate';
import useMutationObserver from './useMutationObserver';
import useMediaQuery from './useMediaQuery';

export {
useRequest,
Expand Down Expand Up @@ -156,4 +157,5 @@ export {
useRafTimeout,
useResetState,
useMutationObserver,
useMediaQuery,
};
42 changes: 42 additions & 0 deletions packages/hooks/src/useMediaQuery/__tests__/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { renderHook, act } from '../../utils/tests';
import useMediaQuery from '..';

Object.defineProperty(window, 'matchMedia', {
writable: true,
value: jest.fn().mockImplementation((query) => ({
matches: false,
media: query,
onchange: null,
addListener: jest.fn(), // deprecated
removeListener: jest.fn(), // deprecated
addEventListener: jest.fn(),
removeEventListener: jest.fn(),
dispatchEvent: jest.fn(),
})),
});

describe('useMediaQuery', () => {
function changeWidth(width: number) {
act(() => {
(global as any).innerWidth = width;
(global as any).dispatchEvent(new Event('resize'));
});
}
changeWidth(1024);

it('should be defined', () => {
expect(useMediaQuery).toBeDefined();
});

it('should return the correct value', () => {
const { result } = renderHook(() => useMediaQuery(['(min-width: 1024px)'], [true], false));
expect(result.current[0]).toBe(false);
});

it('should return the correct value when the media query changes', () => {
const { result } = renderHook(() => useMediaQuery(['(min-width: 1024px)'], [true], false));
expect(result.current[0]).toBe(false);
changeWidth(768);
expect(result.current[0]).toBe(false);
});
});
25 changes: 25 additions & 0 deletions packages/hooks/src/useMediaQuery/demo/demo1.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/**
* title: Get responsive info in components
* desc: By calling useMediaQuery in components, you can retrieve the responsive infomation of the browser page and subscribe to it at the same time.
*
* title.zh-CN: 在组件中获取响应式信息
* desc.zh-CN: 在组件中调用 useMediaQuery 可以获取并订阅浏览器窗口的响应式信息。
*/

import React from 'react';
import { useMediaQuery } from 'ahooks';

export default function () {
const [value, currentQuery] = useMediaQuery(
['(min-width: 1024px)', '(min-width: 768px)', '(min-width: 320px)'],
[3, 2, 1],
0,
);

return (
<div>
<div>currentValue: {value.toString()}</div>
<div>currentQuery: {currentQuery}</div>
</div>
);
}
25 changes: 25 additions & 0 deletions packages/hooks/src/useMediaQuery/index.en-US.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
---
nav:
path: /hooks
---

# useMediaQuery

React Hook for getting responsive info.

## Examples

### Get responsive info in components

<code src="./demo/demo1.tsx" />

## API

```typescript
interface ResponsiveInfo {
value: number;
currentQuery: string;
}

function useMediaQuery(queries: K[], values: T[], defaultValue: T): ResponsiveInfo;
```
32 changes: 32 additions & 0 deletions packages/hooks/src/useMediaQuery/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { useRef, useState } from 'react';

function useMediaQuery<K, T>(queries: K[], values: T[], defaultValue: T) {
const mediaQueryLists = useRef<MediaQueryList[]>([]);
const [value, setValue] = useState<T>(defaultValue);
const [currentQuery, setCurrentQuery] = useState<string>((queries as MediaQueryList[])[0].media);

const getValue = () => {
const index = mediaQueryLists.current.findIndex((mql) => mql.matches);
setCurrentQuery(mediaQueryLists.current?.[index]?.media || '');
return values?.[index] || defaultValue;
};

const handleQueryListener = () => setValue(getValue());

const handleMediaQueryLists = () => {
mediaQueryLists.current.forEach((mql) =>
mql.removeEventListener('change', handleQueryListener),
);
mediaQueryLists.current = queries.map((query) => query && window.matchMedia(query as string));
mediaQueryLists.current.forEach((mql) => mql.addEventListener('change', handleQueryListener));
setValue(getValue());
};

useState(() => {
handleMediaQueryLists();
});

return [value, currentQuery];
}

export default useMediaQuery;
25 changes: 25 additions & 0 deletions packages/hooks/src/useMediaQuery/index.zh-CN.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
---
nav:
path: /hooks
---

# useMediaQuery

获取响应式信息。

## 代码演示

### 在组件中获取响应式信息

<code src="./demo/demo1.tsx" />

## API

```typescript
interface ResponsiveInfo {
value: number;
currentQuery: string;
}

function useMediaQuery(queries: K[], values: T[], defaultValue: T): ResponsiveInfo;
```

0 comments on commit efe0dba

Please sign in to comment.