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
{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;