Skip to content

Commit 77e015b

Browse files
authored
feat: add useDeepCompareEffect hook
2 parents 5b39ab5 + ba9917c commit 77e015b

File tree

7 files changed

+99
-2
lines changed

7 files changed

+99
-2
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@
9292
- [`useMount`](./docs/useMount.md) — calls `mount` callbacks.
9393
- [`useUnmount`](./docs/useUnmount.md) — calls `unmount` callbacks.
9494
- [`useUpdateEffect`](./docs/useUpdateEffect.md) — run an `effect` only on updates.
95+
- [`useDeepCompareEffect`](./docs/useUpdateEffect.md) — run an `effect` depending on deep comparison of its dependencies
9596
<br/>
9697
<br/>
9798
- [**State**](./docs/State.md)

docs/useDeepCompareEffect.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# `useDeepCompareEffect`
2+
3+
A modified useEffect hook that is using deep comparison on its dependencies instead of reference equality.
4+
5+
## Usage
6+
7+
```jsx
8+
import {useCounter, useDeepCompareEffect} from 'react-use';
9+
10+
const Demo = () => {
11+
const [count, {inc: inc}] = useCounter(0);
12+
const options = { step: 2 };
13+
14+
useDeepCompareEffect(() => {
15+
inc(options.step)
16+
}, [options]);
17+
18+
return (
19+
<div>
20+
<p>useDeepCompareEffect: {count}</p>
21+
</div>
22+
);
23+
};
24+
```
25+
26+
## Reference
27+
28+
```ts
29+
useDeepCompareEffect(effect: () => void | (() => void | undefined), deps: any[]);
30+
```

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,15 +35,16 @@
3535
"homepage": "https://github.com/streamich/react-use#readme",
3636
"dependencies": {
3737
"nano-css": "^5.1.0",
38+
"react-fast-compare": "^2.0.4",
3839
"react-wait": "^0.3.0",
3940
"screenfull": "^4.1.0",
4041
"throttle-debounce": "^2.0.1",
4142
"ts-easing": "^0.2.0"
4243
},
4344
"peerDependencies": {
45+
"keyboardjs": "*",
4446
"react": "^16.8.0",
4547
"react-dom": "^16.8.0",
46-
"keyboardjs": "*",
4748
"rebound": "*"
4849
},
4950
"devDependencies": {
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import {storiesOf} from '@storybook/react';
2+
import * as React from 'react';
3+
import {useCounter, useDeepCompareEffect} from '..';
4+
import ShowDocs from './util/ShowDocs';
5+
6+
const Demo = () => {
7+
const [countNormal, {inc: incNormal}] = useCounter(0);
8+
const [countDeep, {inc: incDeep}] = useCounter(0);
9+
const options = {max: 500}
10+
11+
React.useEffect(() => {
12+
if (countNormal < options.max) {
13+
incNormal()
14+
}
15+
}, [options]);
16+
17+
useDeepCompareEffect(() => {
18+
if (countNormal < options.max) {
19+
incDeep()
20+
}
21+
}, [options]);
22+
23+
return (
24+
<div>
25+
<p>useEffect: {countNormal}</p>
26+
<p>useDeepCompareEffect: {countDeep}</p>
27+
</div>
28+
);
29+
};
30+
31+
storiesOf('useDeepCompareEffect', module)
32+
.add('Docs', () => <ShowDocs md={require('../../docs/useDeepCompareEffect.md')} />)
33+
.add('Demo', () => <Demo/>)

src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import useDropArea from './useDropArea';
99
import useCounter from './useCounter';
1010
import useCss from './useCss';
1111
import useDebounce from './useDebounce';
12+
import useDeepCompareEffect from './useDeepCompareEffect';
1213
import useEffectOnce from './useEffectOnce';
1314
import useEvent from './useEvent';
1415
import useFavicon from './useFavicon';
@@ -78,6 +79,7 @@ export {
7879
useCounter,
7980
useCss,
8081
useDebounce,
82+
useDeepCompareEffect,
8183
useEffectOnce,
8284
useEvent,
8385
useFavicon,

src/useDeepCompareEffect.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { useRef, useEffect, EffectCallback, DependencyList } from 'react';
2+
import * as isEqual from 'react-fast-compare';
3+
4+
const isPrimitive = (val: any) => val !== Object(val)
5+
6+
const useDeepCompareEffect = (effect: EffectCallback, deps: any[]) => {
7+
if (process.env.NODE_ENV !== 'production') {
8+
if (!deps || !deps.length) {
9+
console.warn(
10+
'`useDeepCompareEffect` should not be used with no dependencies. Use React.useEffect instead.'
11+
);
12+
}
13+
14+
if (deps.every(isPrimitive)) {
15+
console.warn(
16+
'`useDeepCompareEffect` should not be used with dependencies that are all primitive values. Use React.useEffect instead.'
17+
);
18+
}
19+
}
20+
21+
const ref = useRef<DependencyList | undefined>(undefined);
22+
23+
if (!isEqual(deps, ref.current)) {
24+
ref.current = deps
25+
}
26+
27+
useEffect(effect, ref.current)
28+
}
29+
30+
export default useDeepCompareEffect

yarn.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9226,7 +9226,7 @@ react-error-overlay@^5.1.4:
92269226
resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-5.1.4.tgz#88dfb88857c18ceb3b9f95076f850d7121776991"
92279227
integrity sha512-fp+U98OMZcnduQ+NSEiQa4s/XMsbp+5KlydmkbESOw4P69iWZ68ZMFM5a2BuE0FgqPBKApJyRuYHR95jM8lAmg==
92289228

9229-
react-fast-compare@^2.0.2:
9229+
react-fast-compare@^2.0.2, react-fast-compare@^2.0.4:
92309230
version "2.0.4"
92319231
resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-2.0.4.tgz#e84b4d455b0fec113e0402c329352715196f81f9"
92329232
integrity sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw==

0 commit comments

Comments
 (0)