From 5350e8d9ee6d207f10d342bbf59d4d1cefb62990 Mon Sep 17 00:00:00 2001 From: xobotyi Date: Tue, 30 Jul 2019 23:58:21 +0300 Subject: [PATCH 1/2] useMountedState as a replacement for useRefMounted: - it has more obvious name; - returns a function that check mount state -> more handy ti use due to less to write; - has tests; --- README.md | 3 ++- docs/useMountedState.md | 25 ++++++++++++++++++++ src/__stories__/useMountedState.story.tsx | 17 ++++++++++++++ src/__tests__/useMountedState.test.tsx | 28 +++++++++++++++++++++++ src/index.ts | 2 ++ src/useMountedState.ts | 15 ++++++++++++ 6 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 docs/useMountedState.md create mode 100644 src/__stories__/useMountedState.story.tsx create mode 100644 src/__tests__/useMountedState.test.tsx create mode 100644 src/useMountedState.ts diff --git a/README.md b/README.md index 73793bb9..e8e4f957 100644 --- a/README.md +++ b/README.md @@ -101,11 +101,12 @@ - [`useTitle`](./docs/useTitle.md) — sets title of the page. - [`usePermission`](./docs/usePermission.md) — query permission status for browser APIs.
-
+
- [**Lifecycles**](./docs/Lifecycles.md) - [`useEffectOnce`](./docs/useEffectOnce.md) — a modified [`useEffect`](https://reactjs.org/docs/hooks-reference.html#useeffect) hook that only runs once. - [`useEvent`](./docs/useEvent.md) — subscribe to events. - [`useLifecycles`](./docs/useLifecycles.md) — calls `mount` and `unmount` callbacks. + - [`useMountedState`](./docs/useMountedState.md) — tracks if component is mounted. - [`useRefMounted`](./docs/useRefMounted.md) — tracks if component is mounted. - [`usePromise`](./docs/usePromise.md) — resolves promise only while component is mounted. - [`useLogger`](./docs/useLogger.md) — logs in console as component goes through life-cycles. diff --git a/docs/useMountedState.md b/docs/useMountedState.md new file mode 100644 index 00000000..ae6ecace --- /dev/null +++ b/docs/useMountedState.md @@ -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); + }); +}; +``` diff --git a/src/__stories__/useMountedState.story.tsx b/src/__stories__/useMountedState.story.tsx new file mode 100644 index 00000000..a4bcac43 --- /dev/null +++ b/src/__stories__/useMountedState.story.tsx @@ -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
This component is {isMounted() ? 'MOUNTED' : 'NOT MOUNTED'}
; +}; + +storiesOf('Lifecycle|useMountedState', module) + .add('Docs', () => ) + .add('Demo', () => ); diff --git a/src/__tests__/useMountedState.test.tsx b/src/__tests__/useMountedState.test.tsx new file mode 100644 index 00000000..56981a14 --- /dev/null +++ b/src/__tests__/useMountedState.test.tsx @@ -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(); + }); +}); diff --git a/src/index.ts b/src/index.ts index 6578d00d..bb29fea1 100644 --- a/src/index.ts +++ b/src/index.ts @@ -44,6 +44,7 @@ import useMedia from './useMedia'; import useMediaDevices from './useMediaDevices'; import useMotion from './useMotion'; import useMount from './useMount'; +import useMountedState from './useMountedState'; import useMouse from './useMouse'; import useMouseHovered from './useMouseHovered'; import useNetwork from './useNetwork'; @@ -124,6 +125,7 @@ export { useMediaDevices, useMotion, useMount, + useMountedState, useMouse, useMouseHovered, useNetwork, diff --git a/src/useMountedState.ts b/src/useMountedState.ts new file mode 100644 index 00000000..6236d415 --- /dev/null +++ b/src/useMountedState.ts @@ -0,0 +1,15 @@ +import { useEffect, useRef } from 'react'; + +export default function useMountedState(): () => boolean { + const mountedRef = useRef(false); + + useEffect(() => { + mountedRef.current = true; + + return () => { + mountedRef.current = false; + }; + }); + + return () => mountedRef.current; +} From c2ebba828c68e89734a5af56087880a12f20dd3e Mon Sep 17 00:00:00 2001 From: Vadim Dalecky Date: Sat, 3 Aug 2019 00:58:22 +0200 Subject: [PATCH 2/2] docs: put useMountedState and useRefMouted on one line in README --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index e8e4f957..bea8dc23 100644 --- a/README.md +++ b/README.md @@ -106,8 +106,7 @@ - [`useEffectOnce`](./docs/useEffectOnce.md) — a modified [`useEffect`](https://reactjs.org/docs/hooks-reference.html#useeffect) hook that only runs once. - [`useEvent`](./docs/useEvent.md) — subscribe to events. - [`useLifecycles`](./docs/useLifecycles.md) — calls `mount` and `unmount` callbacks. - - [`useMountedState`](./docs/useMountedState.md) — tracks if component is mounted. - - [`useRefMounted`](./docs/useRefMounted.md) — tracks if component is mounted. + - [`useMountedState`](./docs/useMountedState.md) and [`useRefMounted`](./docs/useRefMounted.md) — track if component is mounted. - [`usePromise`](./docs/usePromise.md) — resolves promise only while component is mounted. - [`useLogger`](./docs/useLogger.md) — logs in console as component goes through life-cycles. - [`useMount`](./docs/useMount.md) — calls `mount` callbacks.