React 把组件看成是一个状态机(State Machines)。通过与用户的交互,实现不同状态,然后渲染 UI,让用户界面和数据保持一致。
React 里,只需更新组件的 state,然后根据新的 state 重新渲染用户界面(不要操作 DOM)。
使用函数方法改变state:简单的显示的时间方法
function Clock(props) {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {props.date.toLocaleTimeString()}.</h2>
</div>
);
}
function tick() {
ReactDOM.render(
<Clock date={new Date()} />,
document.getElementById('root')
);
}
setInterval(tick, 1000); //改变state
使用类方法改变state
将函数转换为类,可以通过5个步骤将函数组件 Clock
转换为类
- 创建一个名称扩展为
React.Component
的ES6 类
- 创建一个叫做
render()
的空方法
- 将函数体移动到
render()
方法中
- 在
render()
方法中,使用this.props
替换props
- 删除剩余的空函数声明
class Clock extends React.Component {
//添加一个"类构造函数"来初始化状态 `this.state`
constructor(props) {
super(props);
this.state = {date: new Date()};
}
//componentDidMount() 钩子建立定时器
componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
1000
);
}
//在 componentWillUnmount()生命周期钩子中卸载计时器
componentWillUnmount() {
clearInterval(this.timerID);
}
tick() {
this.setState({
date: new Date()
});
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>现在是 {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
ReactDOM.render(
<Clock />,
document.getElementById('example')
);
实例解析:
componentDidMount()
与 componentWillUnmount()
方法被称作生命周期钩子。
在组件输出到 DOM 后会执行 componentDidMount()
钩子,我们就可以在这个钩子上设置一个定时器。
this.timerID
为计算器的 ID,我们可以在 componentWillUnmount()
钩子中卸载计算器。
代码执行顺序:
当<Clock />
被传递给 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()
这个钩子函数,定时器也就会被清除。
数据自顶向下流动
父组件或子组件都不能知道某个组件是有状态还是无状态,并且它们不应该关心某组件是被定义为一个函数还是一个类。
这就是为什么状态通常被称为局部或封装。 除了拥有并设置它的组件外,其它组件不可访问
function FormattedDate(props) {
return <h2>现在是 {props.date.toLocaleTimeString()}.</h2>;
}
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>
<FormattedDate date={this.state.date} />
</div>
);
}
}
ReactDOM.render(
<Clock />,
document.getElementById('example')
);
这通常被称为自顶向下
或单向数据流
。 任何状态始终由某些特定组件所有,并且从该状态导出的任何数据或 UI 只能影响树中下方的组件。
如果你想象一个组件树作为属性的瀑布,每个组件的状态就像一个额外的水源,它连接在一个任意点,但也流下来。