每一次渲染都有属于函数组件自己的Props和State
举个计数器栗子:
function Counter() {
const [n, setN] = useState(0);
return (
<div className="App">
<p>You clicked {n} times</p>
<button onClick={() => setN(n + 1)}>+1</button>
</div>
);
}
点击按钮setN后,n加1.你可能会认为实现该功能的原因是n可能是“data binding”, “watcher”, “proxy“或者其他的东西.n会“监听”状态的变化并自动更新.但是事实上n只是一个普普通通的数字而已.
类似于这样:
const n = 0
<p>You clicked {n} times</p>
当组件第一次渲染时,从useState(0)拿到n的初始值为0,当调用setN(n+1)后React会再次渲染组件,这一次从useState拿到n是1
类似于这样
// first render
function Counter() {
const n = 0; // Returned by useState() // ...
<p>You clicked {n} times</p>
// ...
}
// After a click, second render our, function is called again
function Counter() {
const n = 1; // Returned by useState() // ...
<p>You clicked {n} times</p>
// ...
}
// After another click, third render, our function is called again
function Counter() {
const n = 2; // Returned by useState() // ...
<p>You clicked {n} times</p>
// ...
}
当我们更新状态的时候,react会重新渲染组件.每一次渲染都能拿到独立的n 状态,这个状态值是函数中的一个常量。react会更新DOM以保持和渲染输出一致。 因此无任何数据绑定的操作
关键点在于任意一次渲染中的n常量都不会随着时间改变。渲染输出会变是因为我们的组件被一次次调用,每一次调用引起的渲染中,它包含的n值独立于其他渲染。
每一次渲染都有他自己的事件处理函数
举个栗子:
- 操作一:点击 +1 在点击 log
- 操作二:点击 log 再点击 +1
function Counter() {
const [n, setN] = React.useState(0);
const log = () => setTimeout(() => console.log(`n: ${n}`), 3000);
return (
<div className="App">
<p>{n}</p>
<p>
<button onClick={() => setN(n + 1)}>+1</button>
<button onClick={log}>log</button>
</p>
</div>
);
}
操作二log出了旧数据?原因是log捕获了按钮点击时的状态.
我们知道组件函数每次渲染都会被调用,但是每一次调用中n值都是常量,并且它被赋予了当前渲染中的状态值。函数也有类似行为.
先看一个简单的栗子
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);
外层的someone会被赋值很多次(就像在React中,当前的组件状态会改变一样)。然后,在sayHi函数中,局部常量name会和某次调用中的person关联。 因为这个常量是局部的,所以每一次调用都是相互独立的。结果就是,当定时器回调触发的时候,每一个alert都会弹出它拥有的name。
类似于这样
// first render
function Counter() {
const n = 0; // Returned by useState() // ...
const log = () => setTimeout(() => console.log(`n: ${n}`), 3000);
<p>You clicked {n} times</p>
// ...
}
// After a click, second render our, function is called again
function Counter() {
const n = 1; // Returned by useState() // ...
const log = () => setTimeout(() => console.log(`n: ${n}`), 3000);
<p>You clicked {count} times</p>
// ...
}
这就解释了我们的事件处理函数如何捕获了点击时候的n值。也就是说事件处理函数“属于”某一次特定的渲染,当你点击的时候,它会使用那次渲染中n的状态值。
在任意一次渲染中,props和state是始终保持不变的。 如果props和state在不同的渲染中是相互独立的,那么使用到它们的任何值也是独立的(包括事件处理函数)。
每一次渲染都有他自己的Effects
先看个简单的栗子
function Counter() {
const [n, setN] = React.useState(0);
useEffect(() => console.log(`n: ${n}`));
return (
<div className="App">
<p>{n}</p>
<p>
<button onClick={() => setN(n + 1)}>+1</button>
</p>
</div>
);
}
每次setN后就会log出对应的n. 那么effect是如何读取到最新的n 状态值的呢?
effect是否像我们当初猜想n是“data binding”, “watcher”, “proxy“一样存在某种监听机制使得n能够在effect函数内更新?也或许n是一个可变的值,React会在我们组件内部修改它以使我们的effect函数总能拿到最新的值?
都不是。
我们已经知道在任意一次渲染中,props和state是始终保持不变的。n是某个特定渲染中的常量。事件处理函数“看到”的是属于它那次特定渲染中的n状态值。对于effects也同样如此:
并不是n的值在“不变”的effect中发生了改变,而是effect 函数本身在每一次渲染中都不相同。effect“看到”的是属于它那次特定渲染中的n状态值。
以下栗子更能体现 每一次渲染都有他自己的Effects,effect能捕获属于它那次特定渲染中的n状态值.
function Counter() {
const [n, setN] = React.useState(0);
useEffect(() => { setTimeout(() => {
console.log(`You clicked ${n} times`); }, 3000); });
return (
<div className="App">
<p>{n}</p>
<p>
<button onClick={() => setN(n + 1)}>+1</button>
</p>
</div>
);
}
操作:持续点击3次按钮,3秒后依次打印出You clicked 1 times;You clicked 2 times;You clicked 3 times
总结
- 每一次渲染都有他自己的所有。
- 每一个组件内的函数(包括事件处理函数,effects,定时器或者API调用等等)会捕获某次渲染中定义的props和state。
- 在单次渲染的范围内,props和state始终保持不变。
如果想在effect的回调函数里读取最新的值而不是捕获的值。最简单的实现方法是使用refs,