我知道闭包是如何工作的,但我无法理解在没有详尽的依赖项数组的情况下如何在 React 的 useEffect
中创建陈旧的闭包。为此,我试图复制一个陈旧的闭包,就像在 React 的 useEffect
中一样,而不使用 useEffect
,但我无法创建它。我的代码不会创建陈旧的闭包,而是在每个间隔上记录一个正确的值。请您看一下下面的片段并告诉我:
我做错了什么?当我们不提供完整的依赖项数组时,我应该怎么做才能创建一个像我们在 React 的 useEffect
中得到的那样的陈旧闭包? (参考代码在文末)
当我们没有在 useEffect 中给出详尽的依赖时,为什么会创建一个陈旧的闭包?为什么 useEffect
钩子回调中的代码不像普通函数那样只使用词法作用域,并打印实际值?
function createIncrement(incBy) {
let value = 0;
function increment() {
value += incBy;
console.log(value);
}
function useEffect(fun) {
fun()
}
useEffect(function() {
setInterval(function log() {
// what should I do to create a stale closure here?
// So that if I change the value it should show me the old value
// as it does when using React's useEffect without exhaustive dependencies array
console.log(`Count is: ${value}`); // prints correct value each time
}, 2000);
});
setTimeout(() => {
increment(); // increments to 5
increment(); // increments to 6
}, 5000);
return [increment];
}
const [increment] = createIncrement(1);
increment(); // increments to 1
increment(); // increments to 2
increment(); // increments to 3
increment(); // increments to 4
为了完整起见,下面是一个使用 React 的 useEffect 的代码片段,其中我们没有为 React 的 useEffect 提供详尽的依赖数组,因此创建了一个陈旧的闭包:
import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";
import "./styles.css";
function WatchCount() {
const [count, setCount] = useState(0);
useEffect(function () {
setInterval(function log() {
// No matter how many times you increase the counter
// by pressing the button below,
// this will always log count as 0
console.log(`Count is: ${count}`);
}, 2000);
}, []);
return (
<div>
{count}
<button onClick={() => setCount(count + 1)}>Increase</button>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<WatchCount />, rootElement);
答案 0 :(得分:1)
我做错了什么?我应该怎么做才能创建一个陈旧的闭包,例如 当我们不提供完整的时,我们在 React 的 useEffect 中得到的 依赖数组?
您不会得到过时的闭包,因为您只调用了一次 createIncrement
函数。
为了重新渲染一个功能性的react组件,react再次调用组件函数。这将创建一个新的作用域,该作用域与先前调用函数组件时创建的作用域没有链接。
要获得过时的闭包,您需要多次调用 createIncrement
。
function createIncrement(incBy, functionCallIdentifier, intervalDelay) {
let value = 0;
function increment() {
value += incBy;
console.log("inside call: " + functionCallIdentifier + " - value: " + value);
}
increment();
function useEffect(fun) {
fun();
}
useEffect(function () {
setInterval(function log() {
console.log("inside call: " + functionCallIdentifier + " - count: " + value);
}, intervalDelay);
});
}
createIncrement(1, "first call", 2000);
// calling again but 'setInterval' set in the first call will continue
// to log the latest value of variable "value" that it closed over, i.e. 1
createIncrement(2, "second call", 4000);
当我们没有给出详尽的说明时,为什么会创建一个陈旧的闭包 useEffect 中的依赖项?为什么 useEffect 中的代码没有 hook 的回调只使用词法作用域,就像普通函数一样 会,并打印实际值?
上面的代码示例应该让您了解为什么会创建过时的闭包。 useEffect
的回调函数确实使用了词法作用域,但不同的是再次调用函数组件会创建一个新的作用域,但在组件的先前渲染中设置的 useEffect
钩子的旧回调函数将继续查看其创建范围内的值。
此行为并非特定于 react - 这是 JavaScript 中每个函数的行为方式:每个函数都在创建它的范围内关闭。
直到在 useEffect
钩子中设置新回调之前清除前一个回调,旧回调将继续记录其关闭范围内的值。
以下代码示例使用清理函数清除第一次调用 createIncrement
函数时设置的间隔。
function createIncrement(incBy, functionCallIdentifier, useEffectCleanupFn) {
if (useEffectCleanupFn) {
useEffectCleanupFn();
}
let value = 0;
function increment() {
value += incBy;
console.log("inside call: " + functionCallIdentifier + " - value: " + value);
}
increment();
let cleanupFn;
function useEffect(fun) {
cleanupFn = fun();
}
useEffect(function () {
let id = setInterval(function log() {
console.log("inside call: " + functionCallIdentifier + " - count: " + value);
}, 2000);
return () => clearInterval(id);
});
return cleanupFn;
}
let cleanupFn1 = createIncrement(1, "first call");
setTimeout(() => {
createIncrement(2, "second call", cleanupFn1);
}, 4000);