Skip to content

Commit 658addf

Browse files
committed
updated initial state handlers, docs and stories
1 parent eed4215 commit 658addf

9 files changed

Lines changed: 174 additions & 9 deletions

.storybook/addons.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import '@storybook/addon-knobs/register';
1+
import '@storybook/addon-knobs';
22
import '@storybook/addon-options/register';
33
import '@storybook/addon-actions/register';
44
import '@storybook/addon-notes/register';

docs/useDocumentVisibility.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# `useDocumentVisibility`
2+
3+
React sensor hook that tracks document visibility state using the [Page Visibility API](https://developer.mozilla.org/en-US/docs/Web/API/Page_Visibility_API).
4+
5+
## Usage
6+
7+
```jsx
8+
import {useDocumentVisibility} from 'react-use';
9+
10+
const Demo = () => {
11+
const defaultState = document.visibilityState === 'visible';
12+
const isVisible = useDocumentVisibility(defaultState);
13+
14+
return (
15+
<div>
16+
Document is {isVisible ? 'visible' : 'hidden'}
17+
</div>
18+
);
19+
};
20+
```
21+
22+
## Reference
23+
24+
```js
25+
const isVisible = useDocumentVisibility(initialState);
26+
```
27+
28+
- `initialState` &mdash; `boolean`, optional initial state before the actual visibility is determined, defaults to `false`.
29+
- `isVisible` &mdash; `boolean`, whether the document is currently visible (tab is in foreground).

docs/useWindowFocus.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ React sensor hook that tracks if the browser window is focused.
88
import {useWindowFocus} from 'react-use';
99

1010
const Demo = () => {
11-
const isFocused = useWindowFocus(false);
11+
const defaultState = document.hasFocus();
12+
const isFocused = useWindowFocus(defaultState);
1213

1314
return (
1415
<div>

src/useDocumentVisibility.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { useEffect, useState } from 'react';
2+
3+
const useDocumentVisibility = (defaultState: boolean = false) => {
4+
const [isVisible, setIsVisible] = useState(defaultState);
5+
6+
useEffect(() => {
7+
const handleVisibilityChange = () => setIsVisible(document.visibilityState === 'visible');
8+
9+
document.addEventListener('visibilitychange', handleVisibilityChange);
10+
11+
return () => {
12+
document.removeEventListener('visibilitychange', handleVisibilityChange);
13+
};
14+
}, []);
15+
16+
return isVisible;
17+
};
18+
19+
export default useDocumentVisibility;

src/useWindowFocus.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,6 @@ const useWindowFocus = (defaultState: boolean = false) => {
1616
};
1717
}, []);
1818

19-
useEffect(() => {
20-
setIsFocused(document.hasFocus());
21-
}, []);
22-
2319
return isFocused;
2420
};
2521

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { storiesOf } from '@storybook/react';
2+
import * as React from 'react';
3+
import useDocumentVisibility from '../src/useDocumentVisibility';
4+
import ShowDocs from './util/ShowDocs';
5+
6+
const Demo = () => {
7+
const defaultState = document.visibilityState === 'visible';
8+
const isVisible = useDocumentVisibility(defaultState);
9+
10+
return (
11+
<div>
12+
<p>Switch to another browser tab to see the visibility state change.</p>
13+
<div style={{ fontSize: '24px', fontWeight: 'bold' }}>
14+
Document is {isVisible ? '👁️ Visible' : '🙈 Hidden'}
15+
</div>
16+
</div>
17+
);
18+
};
19+
20+
storiesOf('Sensors/useDocumentVisibility', module)
21+
.add('Docs', () => <ShowDocs md={require('../docs/useDocumentVisibility.md')} />)
22+
.add('Demo', () => <Demo />);

stories/useWindowFocus.story.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import { storiesOf } from '@storybook/react';
22
import * as React from 'react';
3-
import { useWindowFocus } from '../src/useWindowFocus';
3+
import useWindowFocus from '../src/useWindowFocus';
44
import ShowDocs from './util/ShowDocs';
55

66
const Demo = () => {
7-
const isFocused = useWindowFocus(false);
7+
const defaultState = document.hasFocus();
8+
const isFocused = useWindowFocus(defaultState);
89

910
return (
1011
<div>
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import { renderHook, act } from '@testing-library/react-hooks';
2+
import useDocumentVisibility from '../src/useDocumentVisibility';
3+
4+
describe('useDocumentVisibility', () => {
5+
const originalVisibilityState = document.visibilityState;
6+
7+
afterEach(() => {
8+
Object.defineProperty(document, 'visibilityState', {
9+
configurable: true,
10+
value: originalVisibilityState,
11+
});
12+
});
13+
14+
it('should be defined', () => {
15+
expect(useDocumentVisibility).toBeDefined();
16+
});
17+
18+
it('should return false initially', () => {
19+
const { result } = renderHook(() => useDocumentVisibility());
20+
21+
expect(result.current).toBe(false);
22+
});
23+
24+
it('should return true initially when initialState is true', () => {
25+
const { result } = renderHook(() => useDocumentVisibility(true));
26+
27+
expect(result.current).toBe(true);
28+
});
29+
30+
it('should return false initially when initialState is false', () => {
31+
const { result } = renderHook(() => useDocumentVisibility(false));
32+
33+
expect(result.current).toBe(false);
34+
});
35+
36+
it('should return true when document becomes visible', () => {
37+
const { result } = renderHook(() => useDocumentVisibility(true));
38+
39+
act(() => {
40+
Object.defineProperty(document, 'visibilityState', {
41+
configurable: true,
42+
value: 'visible',
43+
});
44+
document.dispatchEvent(new Event('visibilitychange'));
45+
});
46+
47+
expect(result.current).toBe(true);
48+
});
49+
50+
it('should return false when document becomes hidden', () => {
51+
const { result } = renderHook(() => useDocumentVisibility());
52+
53+
act(() => {
54+
Object.defineProperty(document, 'visibilityState', {
55+
configurable: true,
56+
value: 'visible',
57+
});
58+
document.dispatchEvent(new Event('visibilitychange'));
59+
});
60+
expect(result.current).toBe(true);
61+
62+
act(() => {
63+
Object.defineProperty(document, 'visibilityState', {
64+
configurable: true,
65+
value: 'hidden',
66+
});
67+
document.dispatchEvent(new Event('visibilitychange'));
68+
});
69+
expect(result.current).toBe(false);
70+
});
71+
72+
it('should add event listener on mount', () => {
73+
const addEventListenerSpy = jest.spyOn(document, 'addEventListener');
74+
75+
renderHook(() => useDocumentVisibility());
76+
77+
expect(addEventListenerSpy).toHaveBeenCalledWith('visibilitychange', expect.any(Function));
78+
79+
addEventListenerSpy.mockRestore();
80+
});
81+
82+
it('should remove event listener on unmount', () => {
83+
const removeEventListenerSpy = jest.spyOn(document, 'removeEventListener');
84+
85+
const { unmount } = renderHook(() => useDocumentVisibility());
86+
unmount();
87+
88+
expect(removeEventListenerSpy).toHaveBeenCalledWith('visibilitychange', expect.any(Function));
89+
90+
removeEventListenerSpy.mockRestore();
91+
});
92+
});

tests/useWindowFocus.test.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,15 @@ describe('useWindowFocus', () => {
1212
expect(result.current).toBe(false);
1313
});
1414

15-
it('should return true initially when initialState is true', () => {
15+
it('should return true initially when defaultState is true', () => {
16+
// Mock document.hasFocus() to return true
17+
const hasFocusSpy = jest.spyOn(document, 'hasFocus').mockReturnValue(true);
18+
1619
const { result } = renderHook(() => useWindowFocus(true));
1720

1821
expect(result.current).toBe(true);
22+
23+
hasFocusSpy.mockRestore();
1924
});
2025

2126
it('should return false initially when initialState is false', () => {

0 commit comments

Comments
 (0)