当所有依赖项都发生变化时,您需要添加一些逻辑来调用您的效果。这是useEffectAllDepsChange,应该可以实现您想要的行为。
这里的策略是将之前的部门与当前的部门进行比较。如果它们不完全不同,我们将之前的 deps 保存在 ref 中,直到它们不同时才更新它。这允许您在调用效果之前多次更改 deps。
import React, { useEffect, useState, useRef } from "react";
// taken from https://usehooks.com/usePrevious/
function usePrevious(value) {
const ref = useRef();
useEffect(() => {
ref.current = value;
}, [value]);
return ref.current;
}
function useEffectAllDepsChange(fn, deps) {
const prevDeps = usePrevious(deps);
const changeTarget = useRef();
useEffect(() => {
// nothing to compare to yet
if (changeTarget.current === undefined) {
changeTarget.current = prevDeps;
}
// we're mounting, so call the callback
if (changeTarget.current === undefined) {
return fn();
}
// make sure every dependency has changed
if (changeTarget.current.every((dep, i) => dep !== deps[i])) {
changeTarget.current = deps;
return fn();
}
}, [fn, prevDeps, deps]);
}
export default function App() {
const [a, setA] = useState(0);
const [b, setB] = useState(0);
useEffectAllDepsChange(() => {
console.log("running effect", [a, b]);
}, [a, b]);
return (
<div>
<button onClick={() => setA((prev) => prev + 1)}>A: {a}</button>
<button onClick={() => setB((prev) => prev + 1)}>B: {b}</button>
</div>
);
}
受 Richard 启发的另一种方法更简洁,但缺点是跨更新渲染更多。
function useEffectAllDepsChange(fn, deps) {
const [changeTarget, setChangeTarget] = useState(deps);
useEffect(() => {
setChangeTarget(prev => {
if (prev.every((dep, i) => dep !== deps[i])) {
return deps;
}
return prev;
});
}, [deps]);
useEffect(fn, changeTarget);
}