From 84973de1a1fec41514b4becec6da0a3f3f241ee7 Mon Sep 17 00:00:00 2001 From: Ward Oosterlijnck Date: Sun, 31 Mar 2019 17:58:44 +1100 Subject: [PATCH] useEffectOnce --- README.md | 1 + docs/useEffectOnce.md | 27 +++++++++++++++++++++++++ src/__stories__/useEffectOnce.story.tsx | 21 +++++++++++++++++++ src/__stories__/util/ConsoleStory.tsx | 7 +++++++ src/index.ts | 2 ++ src/useEffectOnce.ts | 14 +++++++++++++ 6 files changed, 72 insertions(+) create mode 100644 docs/useEffectOnce.md create mode 100644 src/__stories__/useEffectOnce.story.tsx create mode 100644 src/__stories__/util/ConsoleStory.tsx create mode 100644 src/useEffectOnce.ts diff --git a/README.md b/README.md index 4dad9899..74449a3c 100644 --- a/README.md +++ b/README.md @@ -83,6 +83,7 @@

- [**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. - [`useRefMounted`](./docs/useRefMounted.md) — tracks if component is mounted. diff --git a/docs/useEffectOnce.md b/docs/useEffectOnce.md new file mode 100644 index 00000000..bb93bb6e --- /dev/null +++ b/docs/useEffectOnce.md @@ -0,0 +1,27 @@ +# `useEffectOnce` + +React lifecycle hook that runs an effect only once. + +## Usage + +```jsx +import {useEffectOnce} from 'react-use'; + +const Demo = () => { + useEffectOnce(() => { + console.log('Running effect once on mount') + + return () => { + console.log('Running clean-up of effect on unmount') + } + }); + + return null; +}; +``` + +## Reference + +```js +useEffectOnce(effect: EffectCallback); +``` diff --git a/src/__stories__/useEffectOnce.story.tsx b/src/__stories__/useEffectOnce.story.tsx new file mode 100644 index 00000000..43bec8ef --- /dev/null +++ b/src/__stories__/useEffectOnce.story.tsx @@ -0,0 +1,21 @@ +import {storiesOf} from '@storybook/react'; +import * as React from 'react'; +import {useEffectOnce} from '..'; +import ConsoleStory from './util/ConsoleStory' +import ShowDocs from '../util/ShowDocs'; + +const Demo = () => { + useEffectOnce(() => { + console.log('Running effect once on mount') + + return () => { + console.log('Running clean-up of effect on unmount') + } + }); + + return ; +}; + +storiesOf('Lifecycles|useEffectOnce', module) + .add('Docs', () => ) + .add('Demo', () => ) diff --git a/src/__stories__/util/ConsoleStory.tsx b/src/__stories__/util/ConsoleStory.tsx new file mode 100644 index 00000000..7109ce23 --- /dev/null +++ b/src/__stories__/util/ConsoleStory.tsx @@ -0,0 +1,7 @@ +import * as React from 'react'; + +const ConsoleStory = ({message = 'Open the developer console to see logs'}) => ( +

{message}

+); + +export default ConsoleStory diff --git a/src/index.ts b/src/index.ts index d8d15b39..9b8f3640 100644 --- a/src/index.ts +++ b/src/index.ts @@ -9,6 +9,7 @@ import useDropArea from './useDropArea'; import useCounter from './useCounter'; import useCss from './useCss'; import useDebounce from './useDebounce'; +import useEffectOnce from './useEffectOnce'; import useEvent from './useEvent'; import useFavicon from './useFavicon'; import useFullscreen from './useFullscreen'; @@ -77,6 +78,7 @@ export { useCounter, useCss, useDebounce, + useEffectOnce, useEvent, useFavicon, useFullscreen, diff --git a/src/useEffectOnce.ts b/src/useEffectOnce.ts new file mode 100644 index 00000000..50badc6d --- /dev/null +++ b/src/useEffectOnce.ts @@ -0,0 +1,14 @@ +import {useRef, useEffect, EffectCallback} from 'react'; + +const useEffectOnce = (effect: EffectCallback) => { + const didRun = useRef(false); + + useEffect(() => { + if (!didRun.current) { + didRun.current = true; + return effect(); + } + }); +} + +export default useEffectOnce;