Skip to content

Commit

Permalink
feat: add useMountedState hook
Browse files Browse the repository at this point in the history
  • Loading branch information
streamich authored Aug 2, 2019
2 parents 192085a + 5e0ff86 commit 9081b99
Show file tree
Hide file tree
Showing 6 changed files with 88 additions and 2 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
25 changes: 25 additions & 0 deletions docs/useMountedState.md
Original file line number Diff line number Diff line change
@@ -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);
});
};
```
17 changes: 17 additions & 0 deletions src/__stories__/useMountedState.story.tsx
Original file line number Diff line number Diff line change
@@ -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 />);
28 changes: 28 additions & 0 deletions src/__tests__/useMountedState.test.tsx
Original file line number Diff line number Diff line change
@@ -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();
});
});
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
15 changes: 15 additions & 0 deletions src/useMountedState.ts
Original file line number Diff line number Diff line change
@@ -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;
}

0 comments on commit 9081b99

Please sign in to comment.