for (var i=0;i<6;i++){
setTimeout(()=>{
console.log(i)
})
}
for (var i=0;i<6;i++){
!function(i){
setTimeout(()=>{
console.log(i)
})
}(i)
}
React 每一次渲染都有它自己的 Props and State
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
count
会“监听”状态的变化并自动更新吗?这么想可能是学习React的时候有用的第一直觉。
上面例子中,count
仅是一个数字而已。它不是神奇的“data binding”, “watcher”, “proxy”,或者其他任何东西。它就是一个普通的数字像下面这个一样:
// 第一次
function Counter() {
const count = 0;
// ...
<p>You clicked {count} times</p>
// ...
}
// 第二次
function Counter() {
const count = 1;
// ...
<p>You clicked {count} times</p>
// ...
}
// 第三次
function Counter() {
const count = 2;
// ...
<p>You clicked {count} times</p>
// ...
}
当我们更新状态的时候,React
会重新渲染组件。每一次渲染都能拿到独立的 count
状态,这个状态值是函数中的一个常量。
它仅仅只是在渲染输出中插入了 count
这个数字。这个数字由 Reac
t提供。当 setCount
的时候,React
会带着一个不同的 count
值再次调用组件。然后,React
会更新 DOM
以保持和渲染输出一致。
这里关键的点在于任意一次渲染中的 count
常量都不会随着时间改变。渲染输出会变是因为我们的组件被一次次调用,而每一次调用引起的渲染中,它包含的count
值独立于其他渲染。
每一次渲染都有它自己的事件处理函数
function Counter() {
const [count, setCount] = useState(0);
function handleAlertClick() {
setTimeout(() => {
alert('You clicked on: ' + count);
}, 3000);
}
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
<button onClick={handleAlertClick}>
Show alert
</button>
</div>
);
}
你猜 alert
会弹出什么呢?会是 5
吗?— 这个值是 alert
的时候 counter
的实时状态。或者会是 3
吗?— 这个值是我点击时候的状态。
但它究竟是如何工作的呢?
我们发现 count
在每一次函数调用中都是一个常量值。值得强调的是 — 我们的组件函数每次渲染都会被调用,但是每一次调用中 count
值都是常量,并且它被赋予了当前渲染中的状态值。
这并不是 React
特有的,普通的函数也有类似的行为:
function sayHi(person) {
const name = person.name;
setTimeout(() => {
alert('Hello, ' + name);
}, 3000);
}
let someone = {name: 'Dan'};
sayHi(someone);
someone = {name: 'Yuzhi'};
sayHi(someone);
someone = {name: 'Dominic'};
sayHi(someone);
在这个例子会被赋值很多次(就像在 React
中,当前的组件状态会改变一样)。然后,在sayHi
函数中,局部常量name
会和某次调用中的person
关联。因为这个常量是局部的,所以每一次调用都是相互独立的。结果就是,当定时器回调触发的时候,每一个alert都会弹出它拥有的name
。
这就解释了我们的事件处理函数如何捕获了点击时候的count
值。如果我们应用相同的替换原理,每一次渲染“看到”的是它自己的count
// 第一次
function Counter() {
const count = 0;
// ...
function handleAlertClick() {
setTimeout(() => {
alert('You clicked on: ' + count);
}, 3000);
}
// ...
}
// 第二次
function Counter() {
const count = 1;
// ...
function handleAlertClick() {
setTimeout(() => {
alert('You clicked on: ' + count);
}, 3000);
}
// ...
}
// 第三次
function Counter() {
const count = 2;
// ...
function handleAlertClick() {
setTimeout(() => {
alert('You clicked on: ' + count);
}, 3000);
}
// ...
}
所以实际上,每一次渲染都有一个新版本 的 handleAlertClick
。每一个版本的 handleAlertClick
记住 了它自己的 count
每一次渲染都有它自己的…所有
我们现在知道effects会在每次渲染后运行,并且概念上它是组件输出的一部分,可以“看到”属于某次特定渲染的props和state。
function Counter() {
const [count, setCount] = useState(0);
useEffect(() => {
setTimeout(() => {
console.log(`You clicked ${count} times`);
}, 3000);
});
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
小结一下
组件内的每一个函数(包括事件处理函数,effects,定时器或者API调用等等)会捕获定义它们的那次渲染中的props和state。
到目前为止,我们可以明确地知道下面重要的事实:每一个组件内的函数(包括事件处理函数,effects,定时器或者API调用等等)会捕获某次渲染中定义的props和state。
在组件内什么时候去读取props或者state是无关紧要的。因为它们不会改变。在单次渲染的范围内,props和state始终保持不变。(解构赋值的props使得这一点更明显。)
当然,有时候你可能想在effect的回调函数里读取最新的值而不是捕获的值。最简单的实现方法是使用refs。
function Example() {
const [count, setCount] = useState(0);
const latestCount = useRef(count);
useEffect(() => {
// Set the mutable latest value
latestCount.current = count;
setTimeout(() => {
// Read the mutable latest value
console.log(`You clicked ${latestCount.current} times`);
}, 3000);
});
// ...
在React中去直接修改值看上去有点怪异。然而,在 class
组件中 React
正是这样去修改 this.state
的。不像捕获的 props
和 state
,你没法保证在任意一个回调函数中读取的 latestCount.current
是不变的。根据定义,你可以随时修改它。这就是为什么它不是默认行为,而是需要你主动选择这样做。
网友评论