ReactJS 实战之生命周期
然而,它错过了一个关键的要求
Clock 设置一个定时器并且每秒更新 UI 应该是 Clock 的实现细节
理想情况下,我们写一次 Clock 然后它能更新自身
为实现这个需求,我们需要为
Clock
组件添加状态
状态与属性十分相似,但状态是私有的,完全受控于当前组件
局部状态就是:一个功能只适用于类
========================================================================
将函数组件 Clock
转换为类
创建一个名称扩展为
React.Component
的 ES6 类创建一个
render()
空方法将函数体移动到
render()
中在
render()
中,使用this.props
替换props
删除剩余的空函数声明
Clock 现在被定义为一个类而不只是一个函数
使用类就允许我们使用其它特性,例如局部状态、生命周期钩子
===========================================================================
三步将 date 从属性移动到状态中
在
render()
中使用this.state.date
替代this.props.date
添加一个类构造函数来初始化状态
this.state
注意如何传递 props
到基础构造函数的
类组件应始终使用props
调用基础构造函数
从 元素移除 date 属性
稍后将定时器代码添加回组件本身。
结果如下
接下来,我们将使 Clock 设置自己的计时器并每秒更新一次
=============================================================================
在具有许多组件的应用程序中,在销毁时释放组件所占用的资源非常重要
每当Clock
组件第一次加载到 DOM 时,我们都想[生成定时器],这在 React 中被称为挂载
同样,每当Clock
生成的这个 DOM 被移除时,我们也会想要[清除定时器],这在 React 中被称为卸载
我们可以在组件类上声明特殊的方法,当组件挂载或卸载时,来运行一些代码:
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
componentDidMount() {
}
componentWillUnmount() {
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
这些方法被称作生命周期钩子。
当组件输出到 DOM 后会执行 componentDidMount() 钩子,这是一个建立定时器的好地方:
componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
1000
);
}
注意我们是将定时器 ID 保存在 this 中的
尽管 this.props 是由 React 本身安装的以及 this.state 有特殊的含义,如果你需要存储的东西不在数据流中,你可以随意手动向类中添加其他字段(比如定时器 ID)。
我们将在 componentWillUnmount()生命周期钩子中卸载计时器
componentWillUnmount() {
clearInterval(this.timerID);
}
最后,我们实现了每秒钟执行的 tick() 方法。
它将使用 this.setState() 来更新组件局部状态:
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
1000
);
}
componentWillUnmount() {
clearInterval(this.timerID);
}
tick() {
this.setState({
date: new Date()
});
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
ReactDOM.render(
<Clock />,
document.getElementById('root')
);
现在时钟每秒钟都会执行。
让我们快速回顾一下发生了什么以及调用方法的顺序:
当 被传递给 ReactDOM.render() 时,React 调用 Clock 组件的构造函数。 由于 Clock 需要显示当前时间,所以使用包含当前时间的对象来初始化 this.state 。 我们稍后会更新此状态。
React 然后调用 Clock 组件的 render() 方法。这时 React 了解屏幕上应该显示什么内容,然后 React 更新 DOM 以匹配 Clock 的渲染输出。
当 Clock 的输出插入到 DOM 中时,React 调用 componentDidMount() 生命周期钩子。 在其中,Clock 组件要求浏览器设置一个定时器,每秒钟调用一次 tick()。
浏览器每秒钟调用 tick() 方法。 在其中,Clock 组件通过使用包含当前时间的对象调用 setState() 来调度 UI 更新。 通过调用 setState() ,React 知道状态已经改变,并再次调用 render() 方法来确定屏幕上应当显示什么。 这一次,render() 方法中的 this.state.date 将不同,所以渲染输出将包含更新的时间,并相应地更新 DOM。
一旦 Clock 组件被从 DOM 中移除,React 会调用 componentWillUnmount()这个钩子函数,定时器也就会被清除。
========================================================================
关于 setState()
这里有三件事情需要知道
不要直接更新状态
如下代码不会重新渲染组件:
this.state.comment = 'Hello';
应当使用 setState():
this.setState({comment: 'Hello'});
构造器是唯一能够初始化 this.state
的地方。
=========================================================================
React 可以将多个setState()
调用合并成一个调用来提高性能。
因为 this.props
和 this.state
可能是异步更新的,你不应该依靠它们的值来计算下一个状态。
例如,此代码可能无法更新计数器:
this.setState({
counter: this.state.counter + this.props.increment,
});
要修复它,请使用第二种形式的 setState() 来接受一个函数而不是一个对象.
该函数将接收先前的状态作为第一个参数,将此次更新被应用时的 props 做为第二个参数:
// Correct
this.setState((prevState, props) => ({
counter: prevState.counter + props.increment
}));
上方代码使用了箭头函数,但它也适用于常规函数:
constructor(props) {
super(props);
this.state = {
posts: [],
comments: []
};
}
评论