feat: add useMountedState hook

This commit is contained in:
Vadim Dalecky 2019-08-03 01:03:50 +02:00 committed by GitHub
commit 9081b995ff
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 88 additions and 2 deletions

View File

@ -101,12 +101,12 @@
- [`useTitle`](./docs/useTitle.md) — sets title of the page.
- [`usePermission`](./docs/usePermission.md) — query permission status for browser APIs.
<br/>
<br/>
<br/>
- [**Lifecycles**](./docs/Lifecycles.md)
- [`useEffectOnce`](./docs/useEffectOnce.md) &mdash; a modified [`useEffect`](https://reactjs.org/docs/hooks-reference.html#useeffect) hook that only runs once.
- [`useEvent`](./docs/useEvent.md) &mdash; subscribe to events.
- [`useLifecycles`](./docs/useLifecycles.md) &mdash; calls `mount` and `unmount` callbacks.
- [`useRefMounted`](./docs/useRefMounted.md) &mdash; tracks if component is mounted.
- [`useMountedState`](./docs/useMountedState.md) and [`useRefMounted`](./docs/useRefMounted.md) &mdash; track if component is mounted.
- [`usePromise`](./docs/usePromise.md) &mdash; resolves promise only while component is mounted.
- [`useLogger`](./docs/useLogger.md) &mdash; logs in console as component goes through life-cycles.
- [`useMount`](./docs/useMount.md) &mdash; calls `mount` callbacks.

25
docs/useMountedState.md Normal file
View File

@ -0,0 +1,25 @@
# `useMountedState`
Lifecycle hook providing ability to check component's mount state.
Gives a function that will return `true` if component mounted and `false` otherwise.
## Usage
```jsx
import * as React from 'react';
import {useMountedState} from 'react-use';
const Demo = () => {
const isMounted = useMountedState();
React.useEffect(() => {
setTimeout(() => {
if (isMounted()) {
// ...
} else {
// ...
}
}, 1000);
});
};
```

View File

@ -0,0 +1,17 @@
import { storiesOf } from '@storybook/react';
import * as React from 'react';
import { useMountedState } from '..';
import ShowDocs from './util/ShowDocs';
const Demo = () => {
const isMounted = useMountedState();
const [, updateState] = React.useState();
requestAnimationFrame(updateState);
return <div>This component is {isMounted() ? 'MOUNTED' : 'NOT MOUNTED'}</div>;
};
storiesOf('Lifecycle|useMountedState', module)
.add('Docs', () => <ShowDocs md={require('../../docs/useMountedState.md')} />)
.add('Demo', () => <Demo />);

View File

@ -0,0 +1,28 @@
import { renderHook } from 'react-hooks-testing-library';
import useMountedState from '../useMountedState';
describe('useMountedState', () => {
it('should be defined', () => {
expect(useMountedState).toBeDefined();
});
it('should return a function', () => {
const hook = renderHook(() => useMountedState(), { initialProps: false });
expect(typeof hook.result.current).toEqual('function');
});
it('should return true if component is mounted', () => {
const hook = renderHook(() => useMountedState(), { initialProps: false });
expect(hook.result.current()).toBeTruthy();
});
it('should return false if component is unmounted', () => {
const hook = renderHook(() => useMountedState(), { initialProps: false });
hook.unmount();
expect(hook.result.current()).toBeFalsy();
});
});

View File

@ -44,6 +44,7 @@ export { default as useMedia } from './useMedia';
export { default as useMediaDevices } from './useMediaDevices';
export { default as useMotion } from './useMotion';
export { default as useMount } from './useMount';
export { default as useMountedState } from './useMountedState';
export { default as useMouse } from './useMouse';
export { default as useMouseHovered } from './useMouseHovered';
export { default as useNetwork } from './useNetwork';

15
src/useMountedState.ts Normal file
View File

@ -0,0 +1,15 @@
import { useEffect, useRef } from 'react';
export default function useMountedState(): () => boolean {
const mountedRef = useRef<boolean>(false);
useEffect(() => {
mountedRef.current = true;
return () => {
mountedRef.current = false;
};
});
return () => mountedRef.current;
}