[React 基础系列] 状态 & 状态更新 & 生命周期方法
Posted GoldenaArcher
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[React 基础系列] 状态 & 状态更新 & 生命周期方法相关的知识,希望对你有一定的参考价值。
[React 基础系列] 状态 & 状态更新 & 生命周期方法
这一期复习一下 状态(state) 和 生命周期(lifecycle),这一节主要针对的内容是 类组件 中的状态与生命周期,钩子函数(hooks) 中的实现会放在 hooks 中学习。
通过这章的学习,你应该能学会:
- 如何设置状态
- 如何
this.setState()
更新状态 - 常用的几个生命周期方法
之前复习过的内容有:
学习案例下载地址:
[React 基础系列] 状态 & 状态更新 & 生命周期方法-案例
状态
状态(state) 可以用来存放控制组件内的数据变化的变量,如表单内的值就可以存放在状态中去管理。以业务中比较常见的表单逻辑为例,表单中的输入值就很适合放到状态中去管理。
下面模拟一个电话号码为 1301301300
的组件:
class Input extends Component {
state = {
input: '13001301300',
};
render() {
return (
<div>
<label>{this.props.label}</label>
<input type="text" value={this.state.input} />
</div>
);
}
}
ReactDOM.render(
<div className="inputs">
<Input label="电话" />
</div>,
document.getElementById('root')
);
页面的渲染效果看起来如下:
复习一下 props 的相关知识,this.props.label
的值来自于 <Input label="电话" />
中的 label
,是在调用时从父组件传到子组件的参数,值是不可变的。
现在最基础的一个表单组件已经写好了,只是现在这个情况下,因为没有实现对应的事件监听函数,所以,state 中的值是无法被改变的:
同时,打开浏览器的 console 也会出现下列的报错信息,提示要么添加事件管理,要么将 input
中的 value 改成 defaultValue,或者将值设为只读属性:
下一步,就通过添加事件去实现对状态的修改,再去简略的讲一下生命周期机制。
状态更新 - onChange
这里会通过绑定时间的方式去更新状态,更新状态的实现是通过 React 内置的函数:this.setState()
去实现的。这里只是简单的调用一下事件,更复杂的内容会单独拉出来讲。
事件机制的实现如下:
class Input extends Component {
// state 部分不变
// 添加 event handler
updateInputHandler = (e) => {
this.setState({
input: e.target.value,
});
};
// 更新render,主要给 input 添加 onChange
render() {
return (
<div>
<label>{this.props.label}</label>
<input
type="text"
value={this.state.input}
// 新增代码
onChange={(e) => this.updateInputHandler(e)}
/>
</div>
);
}
}
实现 handler 后即可更新状态:
onChange={(e) => this.updateInputHandler(e)}
这样的写法是利用了箭头函数简短和不重写 this
指向 的特性,同时将 event 对象传入到函数中去,并且不会出现立即执行的问题。
在实现了状态更新后,就可以联系一个与状态更新有关的生命周期方法——componentDidUpdate。
注*:React 的设计理念是状态的不可变性,所以默认所有的状态变更,包括捕获状态变更都是通过 this.setState
去实现的。如果直接修改状态不会触发其他生命周期函数——包括 render——的调用。render 如果不被调用,就不会页面的重新渲染。
生命周期方法
下面列举的生命周期方法,相对而言使用的比较频繁的生命周期函数。另外,生命周期函数的调用 不能 使用箭头函数。
constructor
也就是构造函数,通常用来初始化状态以及触发一些副作用(调用其他的函数)。如果不需要在构造函数内触发副作用,只需要初始化状态的情况下,也可以直接通过申明的方法去实现,如:
state = {
input: '13001301300',
};
上面的代码在构造函数内实现,是这样的写法:
class Input extends Component {
// 必须要传入 props 参数
constructor(props) {
// 也必须要调用 super(props)
super(props);
this.state = { input: '13001301300' };
}
}
注,不调用 super(props);
会产生报错:
render
render 也是生命周期函数,它负责渲染整个页面。
页面在挂在过程中,调用完了 constructor 去初始化状态后,就会调用 render 去渲染页面。
componentDidUpdate
在函数更新是调用,这里可以通过参数显示之前的 props、state 和 snapshot,同时也可以通过 this
关键字获取当前的 props 和 state。
这个函数经常会被用来触发副作用,例如说当状态从未登录改为登录是,就可以在 componentDidUpdate 中去调用其他的 API:如获取用户信息、获取登陆用户才能够访问的信息,等。
这里就通过在命令行输出展示一下 componentDidUpdate 的使用方法,代码如下:
class Input extends Component {
componentDidUpdate(prevProps, prevState, snapshot) {
console.log('prevProps', prevProps, 'curr props', this.props);
console.log('prevState', prevState, 'curr state', this.state);
}
}
触发效果如下:
可以看到,prevProps
和 curr props
的值是一样的,但是每次当值变更过后,prevState
和 curr state
的值是由差异的。这个差异就是由状态更新而产生的,prevState
的值是这次更新前的状态,curr state
是更新后的状态。
componentDidUpdate 中也可以通过调用 this.setState()
去触发其他的状态变化,但是需要注意的一点是,在 componentDidUpdate 中触发状态变化一定要注意判断,否则就有可能会产生无限更新,从而导致程序崩溃,如:
class Input extends Component {
componentDidUpdate(prevProps, prevState, snapshot) {
console.log('prevProps', prevProps, 'curr props', this.props);
console.log('prevState', prevState, 'curr state', this.state);
// 每次状态更新都会形成一个新的对象,所以 prevState 和 this.state 永远不会想等
if (prevState !== this.state) {
this.setState({ input: '' });
}
}
}
就会导致无限更新的问题:
componentDidMount
componentDidMount 是一个使用频率也很高的函数,它在页面挂载之后就会立刻被调用,可以在 componentDidMount 函数中触发副作用,即,调用 API 去更新状态。
这里会在 componentDidMount 中调用 setTimeout 去模拟 API 调用发生的延迟状态,随后在 setTimeout 中进行状态更新。
class Input extends Component {
componentDidMount() {
setTimeout(() => {
this.setState({
input: '1357924680',
});
}, 3000);
}
}
在页面渲染(这里就是页面被刷新,模拟一下刚刚打开页面的情况)之后,componentDidMount 会立刻被调用,随后大概在 3 秒中后,电话号码就会从 13001301300
变成 1357924680
。
总结
这章内容主要讲了如何初始化以及在不同的情况下去更新状态,同时简单的讲了一下几个常见的生命周期方法,以及生命周期方法的使用场景。
通过完成这一部分的学习,已经可以实现简单的业务功能了。
以上是关于[React 基础系列] 状态 & 状态更新 & 生命周期方法的主要内容,如果未能解决你的问题,请参考以下文章