React如何更新状态
ReactuseState() 钩子管理功能React组件中的状态。在类组件中this.state 保持状态,你调用特殊方法this.setState() 来更新状态。
大多数情况下,在React中使用状态是直截了当的。然而,在更新状态时,有一个重要的细微差别需要注意。
当你更新组件的状态时,React是立即更新状态(同步)还是安排一个状态更新(异步)?这篇文章回答了这个问题。
1.使用useState() 进行状态更新
考虑一个功能组件DoubleIncreaser 。
import { useState } from 'react';
function DoubleIncreaser() {
const [count, setCount] = useState(0);
const doubleIncreaseHandler = () => {
setCount(count + 1);
setCount(count + 1);
};
return (
<>
<button onClick={doubleIncreaseHandler}>
Double Increase
</button>
<div>Count: {count}</div>
</>
);
}
const [count, setCount] = useState(0) 定义了组件的状态。 setCount()是包含当前状态值的状态变量,而 是状态更新器的函数。
该组件有一个按钮Double Increase。当按钮被点击时,doubleIncreaseHandler 事件处理程序对计数状态进行2次连续递增:setCount(count + 1) ,然后再setCount(count + 1) 。
当点击Double Increase时,该组件的状态是由1 还是2 ?
打开演示,点击双倍增加按钮。每次点击count ,都会增加1 。
当setCount(count + 1) 更新状态时,这些变化不会立即反映在count 变量中。相反,React安排了一次状态更新,在接下来的渲染中,在语句const [count, setCount] = useState(0) ,钩子将新的状态值分配给count 。
例如:如果count 变量是0 ,那么调用setCount(count + 1); setCount(count + 1); 会被简单地评估为setCount(0 + 1); setCount(0 + 1); - 使得下次渲染时的状态为1 。
[value, setValue] = useState()的状态更新函数setValue(newValue)异步地更新状态。
状态更新函数也接受一个回调,用当前状态计算新的状态。如果是DoubleIncreaser ,可以使用setCount(actualCount => actualCount + 1) 。
import { useState } from 'react';
function DoubleIncreaser() {
const [count, setCount] = useState(0);
const doubleIncreaseHandler = () => {
setCount(actualCount => actualCount + 1);
setCount(actualCount => actualCount + 1);
};
return (
<>
<button onClick={doubleIncreaseHandler}>
Double Increase
</button>
<div>Count: {count}</div>
</>
);
}
当使用函数setCount(actualCount => actualCount + 1) 更新状态时,那么actualCount 参数包含状态的实际值。
打开演示,并点击双倍增加按钮。计数的更新由2 ,正如预期的那样。
当然,你可以使用一个中间变量let :
import { useState } from 'react';
function DoubleIncreaser() {
const [count, setCount] = useState(0);
const doubleIncrease = () => {
let actualCount = count;
actualCount = actualCount + 1;
actualCount = actualCount + 1;
setCount(actualCount);
};
return (
<>
<button onClick={this.doubleIncrease}>
Double Increase
</button>
<div>Count: {count}</div>
</>
);
}
let actualCount = count 是一个中间变量,你可以随意更新。更新的中间变量来更新setCount(actualCount)
。
2.状态变量是不可变的和只读的
如果你忘记了状态变量在下一次渲染时的更新,你可能会尝试在改变状态变量后立即读取它。不幸的是,这并不奏效。
function FetchUsers() {
const [users, setUsers] = useState([]);
useEffect(() => {
const startFetching = async () => {
const response = await fetch('/users');
const fetchedUsers = await response.json();
setUsers(fetchedUsers);
console.log(users); // => []
console.log(fetchedUsers); // => ['John', 'Mike', 'Denis']
};
startFetching();
}, []);
return (
<ul>
{users.map(user => <li>{user}</li>)}
</ul>
);
}
FetchUsers 组件在安装时启动一个获取请求-- startFetching()
当获取完成后,setUsers(fetchedUsers) 用获取的用户更新状态。然而,这些变化并没有立即反映在users 状态变量中。
状态变量users 是只读和不可变的。只有useState() 钩子给users 赋值。你不允许手动给状态变量赋值,也不允许改变它。
function FetchUsers() {
const [users, setUsers] = useState([]);
useEffect(() => {
const startFetching = async () => {
const response = await fetch('/users');
const fetchedUsers = await response.json();
users = fetchedUsers; // Incorrect! users is readonly
users.push(..fetchedUsers); // Incorrect! users is immutable
setUsers(fetchedUsers); // Correct!
};
startFetching();
}, []);
return (
<ul>
{users.map(user => <li>{user}</li>)}
</ul>
);
}
3.类组件中的状态更新
异步状态更新的想法对类组件也是有效的。
下面的类组件有一个状态count ,当点击Double Increase按钮的时候,它应该增加2 。
import { Component } from 'react';
class DoubleIncreaser extends Component {
state = {
count: 0
};
render() {
return (
<>
<button onClick={this.doubleIncrease}>
Double Increase
</button>
<div>Count: {this.state.count}</div>
</>
);
}
doubleIncrease = () => {
// Works!
this.setState(({ count }) => ({
count: count + 1
}));
this.setState(({ count }) => ({
count: count + 1
}));
// Won't work!
// this.setState({ count: this.state.count + 1 });
// this.setState({ count: this.state.count + 1 });
}
}
看看doubleIncrease() 事件处理程序:状态更新器也使用了一个回调。
打开演示,点击 "双倍增加"按钮。正如预期的那样,this.state.count 更新了2。
在基于类的组件中,this.state 也不会立即更新。当调用this.setState(newState) ,React会安排一次重新渲染,在下次渲染时this.state ,正好包含新的状态值newState 。
this.setState(newState)异步地更新this.state.
4.4.总结
useState() hook和 this.setState()(在类组件内部)异步更新状态变量和组件的输出。
记住这个简单的规则:
调用
useState()钩子的setter函数setValue(newValue)(或类组件的this.setState())并不完全是更新状态,而是安排一个状态更新。