I am using react useEffect
hooks and checking if an object has changed and only then run the hook again.
My code looks like this.
JavaScript
x
14
14
1
const useExample = (apiOptions) => {
2
const [data, updateData] = useState([]);
3
useEffect(() => {
4
const [data, updateData] = useState<any>([]);
5
doSomethingCool(apiOptions).then(res => {
6
updateData(response.data);
7
})
8
}, [apiOptions]);
9
10
return {
11
data
12
};
13
};
14
Unfortunately it keeps running as the objects are not being recognised as being the same.
I believe the following is an example of why.
JavaScript
1
9
1
const objA = {
2
method: 'GET'
3
}
4
5
const objB = {
6
method: 'GET'
7
}
8
9
console.log(objA === objB)
Perhaps running JSON.stringify(apiOptions)
works?
Advertisement
Answer
Use apiOptions
as state value
I’m not sure how you are consuming the custom hook but making apiOptions
a state value by using useState
should work just fine. This way you can serve it to your custom hook as a state value like so:
JavaScript
1
3
1
const [apiOptions, setApiOptions] = useState({ a: 1 })
2
const { data } = useExample(apiOptions)
3
This way it’s going to change only when you use setApiOptions
.
Example #1
JavaScript
1
25
25
1
import { useState, useEffect } from 'react';
2
3
const useExample = (apiOptions) => {
4
const [data, updateData] = useState([]);
5
6
useEffect(() => {
7
console.log('effect triggered')
8
}, [apiOptions]);
9
10
return {
11
data
12
};
13
}
14
export default function App() {
15
const [apiOptions, setApiOptions] = useState({ a: 1 })
16
const { data } = useExample(apiOptions);
17
const [somethingElse, setSomethingElse] = useState('default state')
18
19
return <div>
20
<button onClick={() => { setApiOptions({ a: 1 }) }}>change apiOptions</button>
21
<button onClick={() => { setSomethingElse('state') }}>
22
change something else to force rerender
23
</button>
24
</div>;
25
}
Alternatively
You could write a deep comparable useEffect
as described here:
JavaScript
1
25
25
1
function deepCompareEquals(a, b){
2
// TODO: implement deep comparison here
3
// something like lodash
4
// return _.isEqual(a, b);
5
}
6
7
function useDeepCompareMemoize(value) {
8
const ref = useRef()
9
// it can be done by using useMemo as well
10
// but useRef is rather cleaner and easier
11
12
if (!deepCompareEquals(value, ref.current)) {
13
ref.current = value
14
}
15
16
return ref.current
17
}
18
19
function useDeepCompareEffect(callback, dependencies) {
20
useEffect(
21
callback,
22
dependencies.map(useDeepCompareMemoize)
23
)
24
}
25
You can use it like you’d use useEffect
.