State & 生命周期

正确使用 State#

不要直接修改 this.state

// 错误示例
this.state.comment = 'Hello';
// 正确示例
this.setState({
comment: "Hello"
});

利用解构语法修改state嵌套对象里的子对象

const { user } = this.state;
user.name.first = "Alan";
this.setState({
user: user
});

state的更新可能是异步的

出于性能考虑, 多个setState调用可能合并成一次调用, 可以在setState的第二个入参传入一个回调函数监听状态更新成功:

import { Component } from "react";
export type CounterProps = {
increment: number
}
type CounterState = {
counter: number
}
export class Counter extends Component<CounterProps, CounterState> {
constructor(props: CounterProps) {
super(props);
this.state = {
counter: 0
}
}
doIncr = () => {
/**
* 假设 this.props.increment 值为 2
* 当点击按钮时, 输出以下日志:
*
* before: 0
* after: 0
* callback: 2
*
* before: 2
* after: 2
* callback: 4
* ....
*
* before 和 after 输出的 this.state.counter 都是更新前的值,
* 而 callback 的日志输出的是最新的值
*/
console.log("before: ", this.state.counter);
this.setState({
counter: this.state.counter + this.props.increment
}, () => {
// 这里读取到的 this.state.counter 是更新后的值
console.log("callback: ", this.state.counter);
});
console.log("after: ", this.state.counter); // 这里读取到的 this.state.counter 值不是更新后的
}
render() {
return ( <b onClick={this.doIncr}>Count: {this.state.counter}</b> );
}
}

因为setState调用可能被合并执行, 所以this.propsthis.state可能会异步更新, 所以不要依赖他们的值来更新下一个状态, 比如上面示例的这段代码可能会无法更新计数器:

import { Component } from "react";
export type CounterProps = {
increment: number
}
type CounterState = {
counter: number
}
export class Counter extends Component<CounterProps, CounterState> {
constructor(props: CounterProps) {
super(props);
this.state = {
counter: 0
}
}
componentDidMount() {
for (let i = 0; i < 100; i++) {
/**
* 错误示例
* 假设 this.props.increment 为1, 最终组件显示的 Count 远远小于100
*/
this.setState({
counter: this.state.counter + this.props.increment
});
}
}
render() {
return (<b>Count: {this.state.counter}</b>);
}
}

要解决这个问题, 可以让 setState() 接收一个函数而不是一个对象. 这个函数用上一个 state 作为第一个参数, 将此次更新被应用时的 props 做为第二个参数:

// 正确示例
this.setState((state, props) => ({
counter: state.counter + props.increment
}));