如何在 react 中"remember"昂贵的递归函数中先前计算的值?



假设我有一个这样的递归回调(但更复杂):

const weightedFactorial = useCallback(n => {
if (n === 0) {
return 1;
}
return weight * n * weightedFactorial(n - 1);
},[weight]);

是否有一种方法来存储以前计算的值,这样如果函数在重复索引上调用,就可以跳过递归?

const value1 = weightedFactorial(60) 
// Calculate recursively
const value2 = weightedFactorial(30) 
// This value should already be known 
// and the calculation should be skipped

我试图保持一个已知值的状态,但我似乎陷入了一个循环,可能是因为它需要是回调的依赖,

const [knownValues, setKnownValues] = useState([{ n: 0, value: 1 }]);
const weightedFactorial = useCallback(n => {
const known = knownValues.find(known => known.n === n);
if (known?.value) {
return known.value;
}
const newValue = weight * n * weightedFactorial(n - 1);
setKnownValues(knownValues => [...knownValues, { n, value: newValue }]);
return newValue;
},[knownValues, weight]);

存储结果的最简单方法是Map,如果函数参数是正整数,则使用数组:

function FactorialExamples(props) {
const results = [1];
const factorial = n => {
if (n in results) return results[n]; // also takes care of our base case n==0
return results[n] = n * factorial(n-1);
};

return <p>
5!: { factorial(5) }
<br />
10!: { factorial(10) }
</p>;
}

如果多次调用factorial,这将很好地工作,并且不会超过一次计算阶乘。但是,它仍然会在每次呈现函数组件时重做计算。要在重新渲染时持久化结果,请将results变量从函数移到模块作用域,或者将其放入引用中(每个安装的组件将存储一次):

function Factorial(props) {
const results = useRef([1]).current;
const factorial = n => {
if (n in results) return results[n];
return results[n] = n * factorial(n-1);
};
return <p>
{props.n}!: { factorial(props.n) }
</p>;
}
function Demo() {
const [value, setValue] = useState(0);
return <div>
<input type="number" value={value} onInput=(e => setValue(e.currentTarget.valueAsNumber)} min="0" />
<Factorial n={value} />
</div>;
}

但是你有一个更复杂的情况:weightedFactorial也取决于weight状态,而不仅仅取决于它的参数1。您可以在每次weight更改时重置引用,但这很复杂且容易出错。

相反,使用类似于useCallback的方法,将结果存储与"回调"一起切换出来。用useMemo代替useCallback,并从它返回一个闭包:

const weightedFactorial = useMemo(() => {
const results = [1];
return n => {
if (n in results) return results[n]; 
return weight * n * weightedFactorial(n - 1);
};
}, [weight]);

1:当然,您可以将weightn都视为参数,并使用标准方法来记忆具有多个参数的函数(参见此处)。然后,结果可以再次静态存储或存储在引用中。

您可以使用React.useMemo来计算第一次渲染或依赖项更改时的值。但也许你指的是memoization,以便以前计算的值不需要其他计算。https://www.30secondsofcode.org/js/s/memoize或https://www.digitalocean.com/community/tutorials/js-understanding-recursion

你可以结合useMemomemoization来实现你需要的。

最新更新