我正在使用react useEffect
钩子,并检查对象是否已更改,然后才再次运行该钩子。
我的代码如下。
const useExample = (apiOptions) => {
const [data, updateData] = useState([]);
useEffect(() => {
const [data, updateData] = useState<any>([]);
doSomethingCool(apiOptions).then(res => {
updateData(response.data);
})
}, [apiOptions]);
return {
data
};
};
不幸的是,由于无法识别出相同的对象,它一直在运行。
我相信以下是一个示例。
const objA = {
method: 'GET'
}
const objB = {
method: 'GET'
}
console.log(objA === objB)
也许运行JSON.stringify(apiOptions)
有用吗?
答案 0 :(得分:6)
如果输入足够浅,以至于您认为深度相等仍然很快,请考虑使用JSON.stringify
:
const useExample = (apiOptions) => {
const [data, updateData] = useState([]);
const apiOptionsJsonString = JSON.stringify(apiOptions);
useEffect(() => {
const apiOptionsObject = JSON.parse(apiOptionsJsonString);
doSomethingCool(apiOptionsObject).then(response => {
updateData(response.data);
})
}, [apiOptionsJsonString]);
return {
data
};
};
请注意,它不会比较功能。
答案 1 :(得分:5)
apiOptions
作为状态值我不确定您如何使用自定义钩子,但是通过使用apiOptions
使useState
成为状态值应该可以正常工作。这样,您可以将其作为状态值提供给自定义钩子,如下所示:
const [apiOptions, setApiOptions] = useState({ a: 1 })
const { data } = useExample(apiOptions)
这样,只有当您使用setApiOptions
时,它才会改变。
示例1
import { useState, useEffect } from 'react';
const useExample = (apiOptions) => {
const [data, updateData] = useState([]);
useEffect(() => {
console.log('effect triggered')
}, [apiOptions]);
return {
data
};
}
export default function App() {
const [apiOptions, setApiOptions] = useState({ a: 1 })
const { data } = useExample(apiOptions);
const [somethingElse, setSomethingElse] = useState('default state')
return <div>
<button onClick={() => { setApiOptions({ a: 1 }) }}>change apiOptions</button>
<button onClick={() => { setSomethingElse('state') }}>
change something else to force rerender
</button>
</div>;
}
您可以按照here的描述来编写深度比较深的useEffect
:
function deepCompareEquals(a, b){
// TODO: implement deep comparison here
// something like lodash
// return _.isEqual(a, b);
}
function useDeepCompareMemoize(value) {
const ref = useRef()
// it can be done by using useMemo as well
// but useRef is rather cleaner and easier
if (!deepCompareEquals(value, ref.current)) {
ref.current = value
}
return ref.current
}
function useDeepCompareEffect(callback, dependencies) {
useEffect(callback, useDeepCompareMemoize(dependencies))
}
您可以像使用useEffect
一样使用它。
答案 2 :(得分:3)
我刚刚找到了适合我的解决方案。
您必须使用Lodash的usePrevious()
和_.isEqual()
。
在useEffect()
内,您设置一个条件,如果上一个 apiOptions
等于当前 apiOptions
。如果为true,则不执行任何操作。如果为 updateData()。
示例:
const useExample = (apiOptions) => {
const myPreviousState = usePrevious(apiOptions);
const [data, updateData] = useState([]);
useEffect(() => {
if (myPreviousState && !_.isEqual(myPreviousState, apiOptions)) {
updateData(apiOptions);
}
}, [apiOptions])
}
usePrevious(value)
是一个自定义挂钩,可使用ref
创建一个useRef()
。
您可以从Official React Hook documentation中找到它。
const usePrevious = value => {
const ref = useRef();
useEffect(() => {
ref.current = value;
});
return ref.current;
};
答案 3 :(得分:1)
您可以使用useDeepCompareEffect,useCustomCompareEffect或编写自己的钩子。
https://github.com/kentcdodds/use-deep-compare-effect https://github.com/sanjagh/use-custom-compare-effect
答案 4 :(得分:0)
如果您确实确定无法控制@PowerMockIgnore
,则只需将本机useEffect替换为https://github.com/kentcdodds/use-deep-compare-effect。
答案 5 :(得分:0)
假设您的 apiOptions
的很大一部分是静态的,并且在组件重新渲染时不会改变,我建议采用以下方法:
const useExample = (apiOptions, deps) => {
useEffect(callback, deps);
return { ... };
};
这个想法是只传输效果所依赖的值数组作为第二个参数