1 如何封装真正可复用的 Clock 组件
1.1 封装时钟的外观
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);
Clock
组件需要设置一个计时器,并且需要每秒更新 UI
实现 Clock
组件自我更新
ReactDOM.render(
<Clock />,
document.getElementById('root')
);
-
需要在
Clock
组件中添加state
来实现组件的自我更新 -
State
与props
类似,但是state
是私有的,并且完全受控于当前组件
1.2 将函数组件转换成 class 组件
class Clock extends React.Component {
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is { this.props.date.toLocaleTimeString() }.</h2>
</div>
);
}
}
- 每次组件更新时
render
方法都会被调用,但只要在相同的 DOM 节点中渲染,就仅有一个Clock
组件的 class 实例被创建使用
1.3 向 class 组件中添加局部的 state
- 通过以下三步将
date
从props
移动到state
中
1. 把 render()
方法中的 this.props.date
替换成 this.state.date
class Clock extends React.Component {
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is { this.state.date.toLocaleTimeString() }.</h2>
</div>
);
}
}
2. 添加一个 class 构造函数,然后在该函数中为 this.state
赋初值
class Clock extends React.Component {
// 将props传递到父类的构造函数中
constructor(props) {
super(props)
this.state = { date: new Date() }
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is { this.state.date.toLocaleTimeString() }.</h2>
</div>
);
}
}
3. 移除 <Clock />
元素中的 date 属性
ReactDOM.render(
<Clock />,
document.getElementById('root')
)
1.4 将生命周期方法添加到 class 中
1. 在组件被挂载到 DOM 后运行
componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
1000
)
}
2. 在组件卸载后清除计时器
componentWillUnmount() {
clearInterval(this.timerID)
}
3. 使用 this.setState()
来时刻更新组件 state
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>
);
}
}
RenderDOM.render(
<Clock />,
document.getElementById('root')
)
2 正确使用 State
2.1 不要直接修改 state
// wrong
this.state.commit = 'Hello'
// Correct
this.setState({
commit: 'Hello'
})
- 构造函数是唯一可以给
this.state
赋值的地方
2.2 State 的更新可能是异步的
-
出于性能考虑,react 可能会把多个
setState()
调用合并成一个调用 -
要解决异步调用的问题,可以让
setState()
接收一个函数而不是一个对象,这个函数用上一个state
作为第一个参数,将此次更新被应用时的props
作为第二个参数
// wrong
this.setState({
counter: this.state.counter + this.props.increment
})
// Correct
this.setState((state, props) => {
counter: state.counter + props.increment
})
2.3 State 的更新会被合并
2.4 数据是向下流动的
- 任何的
state
总是所属于特定的组件,而且从该state
派生的任何数据或 UI 只能影响树中“低于”它们的组件