diff --git a/README.md b/README.md
index 74449a3cc6..4bef7df805 100644
--- a/README.md
+++ b/README.md
@@ -92,6 +92,7 @@
- [`useMount`](./docs/useMount.md) — calls `mount` callbacks.
- [`useUnmount`](./docs/useUnmount.md) — calls `unmount` callbacks.
- [`useUpdateEffect`](./docs/useUpdateEffect.md) — run an `effect` only on updates.
+ - [`useDeepCompareEffect`](./docs/useUpdateEffect.md) — run an `effect` depending on deep comparison of its dependencies
- [**State**](./docs/State.md)
diff --git a/docs/useDeepCompareEffect.md b/docs/useDeepCompareEffect.md
new file mode 100644
index 0000000000..9ed37c4d8b
--- /dev/null
+++ b/docs/useDeepCompareEffect.md
@@ -0,0 +1,30 @@
+# `useDeepCompareEffect`
+
+A modified useEffect hook that is using deep comparison on its dependencies instead of reference equality.
+
+## Usage
+
+```jsx
+import {useCounter, useDeepCompareEffect} from 'react-use';
+
+const Demo = () => {
+ const [count, {inc: inc}] = useCounter(0);
+ const options = { step: 2 };
+
+ useDeepCompareEffect(() => {
+ inc(options.step)
+ }, [options]);
+
+ return (
+
+
useDeepCompareEffect: {count}
+
+ );
+};
+```
+
+## Reference
+
+```ts
+useDeepCompareEffect(effect: () => void | (() => void | undefined), deps: any[]);
+```
diff --git a/package.json b/package.json
index dac3b80bf3..555093a651 100644
--- a/package.json
+++ b/package.json
@@ -35,15 +35,16 @@
"homepage": "https://github.com/streamich/react-use#readme",
"dependencies": {
"nano-css": "^5.1.0",
+ "react-fast-compare": "^2.0.4",
"react-wait": "^0.3.0",
"screenfull": "^4.1.0",
"throttle-debounce": "^2.0.1",
"ts-easing": "^0.2.0"
},
"peerDependencies": {
+ "keyboardjs": "*",
"react": "^16.8.0",
"react-dom": "^16.8.0",
- "keyboardjs": "*",
"rebound": "*"
},
"devDependencies": {
diff --git a/src/__stories__/useDeepCompareEffect.story.tsx b/src/__stories__/useDeepCompareEffect.story.tsx
new file mode 100644
index 0000000000..1035a040af
--- /dev/null
+++ b/src/__stories__/useDeepCompareEffect.story.tsx
@@ -0,0 +1,33 @@
+import {storiesOf} from '@storybook/react';
+import * as React from 'react';
+import {useCounter, useDeepCompareEffect} from '..';
+import ShowDocs from './util/ShowDocs';
+
+const Demo = () => {
+ const [countNormal, {inc: incNormal}] = useCounter(0);
+ const [countDeep, {inc: incDeep}] = useCounter(0);
+ const options = {max: 500}
+
+ React.useEffect(() => {
+ if (countNormal < options.max) {
+ incNormal()
+ }
+ }, [options]);
+
+ useDeepCompareEffect(() => {
+ if (countNormal < options.max) {
+ incDeep()
+ }
+ }, [options]);
+
+ return (
+
+
useEffect: {countNormal}
+
useDeepCompareEffect: {countDeep}
+
+ );
+};
+
+storiesOf('useDeepCompareEffect', module)
+ .add('Docs', () => )
+ .add('Demo', () => )
diff --git a/src/index.ts b/src/index.ts
index 9b8f364024..272a20b7c0 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 useDeepCompareEffect from './useDeepCompareEffect';
import useEffectOnce from './useEffectOnce';
import useEvent from './useEvent';
import useFavicon from './useFavicon';
@@ -78,6 +79,7 @@ export {
useCounter,
useCss,
useDebounce,
+ useDeepCompareEffect,
useEffectOnce,
useEvent,
useFavicon,
diff --git a/src/useDeepCompareEffect.ts b/src/useDeepCompareEffect.ts
new file mode 100644
index 0000000000..48923a40f0
--- /dev/null
+++ b/src/useDeepCompareEffect.ts
@@ -0,0 +1,30 @@
+import { useRef, useEffect, EffectCallback, DependencyList } from 'react';
+import * as isEqual from 'react-fast-compare';
+
+const isPrimitive = (val: any) => val !== Object(val)
+
+const useDeepCompareEffect = (effect: EffectCallback, deps: any[]) => {
+ if (process.env.NODE_ENV !== 'production') {
+ if (!deps || !deps.length) {
+ console.warn(
+ '`useDeepCompareEffect` should not be used with no dependencies. Use React.useEffect instead.'
+ );
+ }
+
+ if (deps.every(isPrimitive)) {
+ console.warn(
+ '`useDeepCompareEffect` should not be used with dependencies that are all primitive values. Use React.useEffect instead.'
+ );
+ }
+ }
+
+ const ref = useRef(undefined);
+
+ if (!isEqual(deps, ref.current)) {
+ ref.current = deps
+ }
+
+ useEffect(effect, ref.current)
+}
+
+export default useDeepCompareEffect
diff --git a/yarn.lock b/yarn.lock
index 48e8b2d57b..75bd4d6253 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -9226,7 +9226,7 @@ react-error-overlay@^5.1.4:
resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-5.1.4.tgz#88dfb88857c18ceb3b9f95076f850d7121776991"
integrity sha512-fp+U98OMZcnduQ+NSEiQa4s/XMsbp+5KlydmkbESOw4P69iWZ68ZMFM5a2BuE0FgqPBKApJyRuYHR95jM8lAmg==
-react-fast-compare@^2.0.2:
+react-fast-compare@^2.0.2, react-fast-compare@^2.0.4:
version "2.0.4"
resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-2.0.4.tgz#e84b4d455b0fec113e0402c329352715196f81f9"
integrity sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw==