新的React API包含useEffect()
,其第二个自变量采用Object
,React进行比较以查看组件是否已更新。
例如
useEffect(
() => {
const subscription = props.source.subscribe();
return () => {
subscription.unsubscribe();
};
},
[props.source],
);
其中[props.source]
是所讨论的参数。
我的问题是:我可以定义一个自定义函数来运行以检查道具是否已更改吗?
我有一个自定义对象,React似乎无法告诉它何时已更改。
答案 0 :(得分:1)
AFAIK,目前无法实现。有一些解决方法:
1)在useEffect
内部手动进行深度比较。保存上一个。您可以使用useState
或什至更好的useRef
值,如此处所示:https://overreacted.io/a-complete-guide-to-useeffect/
2)使用JSON.stringify(props.source)
进行哈希处理。如果数据不是太大,可以的。请注意,stringify
可能会产生不一致的结果(对象更改顺序中的键将更改输出)。
3)使用md5(props.source)
进行哈希处理(或其他一些快速/轻度哈希处理)。比以前更容易实现,但速度较慢。
答案 1 :(得分:0)
该答案的积分转到@Tholle use object in useEffect 2nd param without having to stringify it to JSON
const { useState, useEffect, useRef } = React;
const { isEqual } = _;
function useDeepEffect(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);
}
function App() {
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"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div id="root"></div>
useRef
代替useState
通过使用useState返回的函数,组件将被重新渲染,在这种情况下,这是不需要的
答案 2 :(得分:0)
从其他答案中汲取灵感,我一直在使用以下钩子
import React from 'react';
import IsEqual from '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(): *}
*/
const useMostRecentImmutableValue = (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 changes
if (!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;
};
};
export default useMostRecentImmutableValue;
然后在一个钩子中我会做这样的事情:
import useMostRecentImmutableValue from 'use-most-recent-immutable-value.hook';
const useMyHook = (foo = { 'hi': 5 }) => {
const mostRecentFoo = useMostRecentImmutableValue(foo);
React.useEffect(() => {
const unsubscribe = mySubscriptionFunction(mostRecentFoo);
return () => {
unsubscribe();
};
}, [mostRecentFoo]);
};
export default useMyHook;