Custom Useeffect Second Argument
Solution 1:
AFAIK it's not currently possible. There are some workarounds:
1) Do deep comparison manually inside useEffect
. To store the prev. value you may use useState
or, even better, useRef
as demonstrated here: https://overreacted.io/a-complete-guide-to-useeffect/
2) Hashing with JSON.stringify(props.source)
. Can be fine, if the data is not too big. Note that stringify
may produce inconsistent results (keys in objects changing order will change the output).
3) Hashing with md5(props.source)
(or some other quick/light hashing). More realiable yet slower than the previous.
Solution 2:
Credit for this answer goes to @Tholle use object in useEffect 2nd param without having to stringify it to JSON
const { useState, useEffect, useRef } = React;
const { isEqual } = _;
functionuseDeepEffect(fn, deps) {
const isFirst = useRef(true);
const prevDeps = useRef(deps);
useEffect(() => {
const isSame = prevDeps.current.every((obj, index) =>isEqual(obj, deps[index])
);
if (isFirst.current || !isSame) {
fn();
}
isFirst.current = false;
prevDeps.current = deps;
}, deps);
}
functionApp() {
const [state, setState] = useState({ foo: "foo" });
useEffect(() => {
setTimeout(() =>setState({ foo: "foo" }), 1000);
setTimeout(() =>setState({ foo: "bar" }), 2000);
}, []);
useDeepEffect(() => {
console.log("State changed!");
}, [state]);
return<div>{JSON.stringify(state)}</div>;
}
ReactDOM.render(<App />, document.getElementById("root"));
<scriptsrc="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script><scriptsrc="https://unpkg.com/react@16/umd/react.development.js"></script><scriptsrc="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script><divid="root"></div>
Why use useRef
instead of useState
By using the function returned from useState the component will be re-rendered, which is not desired in this case
Solution 3:
Taking inspiration from other answers, I've been using the following hook
importReactfrom'react';
importIsEqualfrom'lodash/isEqual';
/**
* The role of this function is to ensure that useEffect doesn't not
* fire unless the value of a varr (variable but "var" is a reserved word 😉)
* changes. For examples:
* const a = { 'hi': 5 }
* const b = { 'hi': 5 }
*
* a === b // false
* IsEqual(a, b) // true
*
* By default the React.useEffect(() => {}, [dependencies]) only does "===" but
* for cases where we only want useEffect to re-fire when the true value
* of a varr changes "IsEqual", we use this.
*
* @param varr: any
* @returns {function(): *}
*/constuseMostRecentImmutableValue = (varr) => {
let mostRecentValue = varr;
const mostRecentPointer = React.useRef(varr);
return() => {
// short circuit if "shallow equality"if (mostRecentPointer.current === varr) {
return mostRecentValue;
}
// mostRecentValue only updates when the true value of varr changesif (!IsEqual(varr, mostRecentPointer.current)) {
mostRecentValue = varr;
}
// mostRecentPointer changes every time "shallow equality" fails but// after we've checked deep equality
mostRecentPointer.current = varr;
return mostRecentValue;
};
};
exportdefault useMostRecentImmutableValue;
And then in a hook I would do something like this:
import useMostRecentImmutableValue from'use-most-recent-immutable-value.hook';
constuseMyHook = (foo = { 'hi': 5 }) => {
const mostRecentFoo = useMostRecentImmutableValue(foo);
React.useEffect(() => {
const unsubscribe = mySubscriptionFunction(mostRecentFoo);
return() => {
unsubscribe();
};
}, [mostRecentFoo]);
};
exportdefault useMyHook;
Post a Comment for "Custom Useeffect Second Argument"