6.2.1 实例CountDown
您可以在百度里搜索“深入浅出React和Redux 艾草文学(www.321553.xyz)”查找最新章节!
6.2.1 实例CountDown
上面的AddUserProp太简单,让我们利用“以函数为子组件”模式来构建一个复杂一点的例子CountDown,实现倒计时的通用功能。
倒计时功能可以应用在很多场合中:在一个演示页面中,只是简单变化一个数字而已;在临近新年钟声响起的时候,倒计时的表现形式就是在大屏幕上显示动画的10、9、8……3、2、1倒数;在一个游戏场景里,倒计时的表现形式是一个炸弹上的电子计时器逐渐减少为0。
在这些场景下,如果我们要求所有子组件都接受一个固定的prop,那就显得太不灵活了,该是“以函数为子组件”模式上场的时候了。
我们要定义一个名为CountDown的组件,这个CountDown可以接受一个初始值作为当前数字,然后每隔一秒钟调用一次函数形式的子组件,同时把当前数字减1,如此重复,直到当前数字减为0。
下面是CountDown组件的代码,首先CounterDown是一个React组件,构造函数代码如下:
class CountDown extends React.Component {
constructor() {
super(...arguments);
this.state = {count: this.props.startCount};
}
}
在我们的CountDown中,需要有一个倒数的开始值,所以还需要一个名为start-Count的prop。CountDown发挥作用靠的是持续驱动子组件重新渲染。要让子组件重新渲染,CountDown的更新过程要被驱动,所以我们需要CountDown包含状态,这个状态叫做count,在构造函数里我们把它初始化为和startCount一样的prop值。
当CountDown组件完成装载时,在componentDidMount函数中通过setInterval函数启动每秒钟更新内部状态的操作,代码如下:
componentDidMount() {
this.intervalHandle = setInterval(() => {
const newCount = this.state.count - 1;
if (newCount >= 0) {
this.setState({count: newCount});
} else {
window.clearInterval(this.intervalHandle);
}
}, 1000);
}
为了让CountDown每隔一秒驱动一次更新过程,我们就要利用JavaScript的set-Interval函数,我们必须记录下setInterval的返回结果。因为不能无限制地重复倒数,当状态count减小为0,或者CountDown组件实例被卸载的时候,就必须取消setInterval引发的重复操作,我们把setInterval返回的结果记录在组件类的成员变量intervalHandle上。
为什么我们用组件类的成员变量记录intervalHandle,而用组件状态记录count呢?
虽然成员变量和组件状态都是特定于某个组件实例的数据,但是组件状态的改变可以引发组件的更新过程,而普通的成员变量不会,所以实在没有理由把intervalHandle放在组件状态中。
值得一说的是,setInterval帮我们把第一个函数参数中的环境this设为组件实例,我们之所以能够在那个函数中直接通过this访问this.state和this.setState,是因为我们setInterval第一个参数是ES6的箭头函数形式,箭头函数会自动将自身的this绑定为所处环境的this,因为这个箭头函数所处环境是componentDidMount,所以this自然就是组件实例本身。
在componentDidUnmount里面一定要取消并且清理掉intervalHandle,因为一个Count-Down组件完全可能在没有倒计时到0的时候就被卸载,如果不在componentDid-Unmount取消setInterval引发的定时间隔操作,那么这个定时间隔操作会一直继续,继续去驱动子组件的重新执行,这会引发无法预料的后果。componentDidUnmount函数的代码如下:
componentWillUnmount() {
if (this.intervalHandle) {
window.clearInterval(this.intervalHandle);
}
}
在CountDown的render函数中,是这个模式最重要的部分,调用this.props.children,把需要传递进去的this.state.count作为参数带上,代码如下:
render() {
return this.props.children(this.state.count);
}
CountDown组件需要两个prop,其中children代表函数类型的子组件,startCount为初始化的数值,PropType的代码如下:
CountDown.propTypes = {
children: React.PropTypes.func.isRequired,
startCount: React.PropTypes.number.isRequired
}
定义好CountDown组件之后,就可以将其应用于任何需要倒计时的场合,所要做的只是定义恰当的函数作为CountDown子组件而已。
一个简单的显示倒计时从10到0的例子如下:
{
(count) => {count}
}
类似于新年倒计时的例子,当倒计时为0的时候,显示“新年快乐”:
{
(count) => { count > 0 ? count : '新年快乐'}
}
类似于炸弹倒计时的例子,完全把倒数数字交给Bomb子类:
{
(count) =>
}
可以看到,使用CountDown的方法非常灵活。
从CountDown的例子中我们看出一点端倪,这种“以函数为子组件”的模式非常适合于制作动画,类似CountDown这样的例子决定动画每一帧什么时候绘制,绘制的时候是什么样的数据,作为子组件的函数只要专注于使用参数来渲染就可以了。
实际上,React实际中的动画库react-motion就大量使用了“以函数为子组件”的模式,我们在第10章中会详细介绍。 深入浅出React和Redux