React把组件看成一个状态机(State Machines).通过与用户的交互,实现不同状态,然后渲染UI,让用户界面和数据保持一致。
React里,只需要更新组件的state,然后根据新的state重新渲染用户界面(不要操作DOM)。
以下实例创建一个名称扩展为React.Component的ES6类,在render()方法中使用this.state来修饰当前的时间。
添加一个类构造函数来初始化状态this.state,类组件始终使用props调用基础构造函数.
1 | class Clock extends React.Component{ |
接下来,我们将使Clock设置自己的计时器并每秒更新一次。
将生命周期方法添加到类中
在具有许多组件的应用程序中,在销毁时释放组件所占用的资源非常重要。
每当Clock组件第一次加载到DOM中的时候,我们都想生成定时器,这在React中被称为挂载。
同样,每当Clock生成的这个DOM被移除的时候,我们也会想要清除定时器,这在React中被称为卸载。
我们可以在组件类上声明特殊的方法,当组件挂载或卸载时,来运行一些代码:
1 | class Clock extends React.Component{ |
实例解析:componentDidMount()
与compoentWillUnmount()
方法被成为生命周期钩子。在组件输出到DOM后会执行componentDidMount()
钩子,我们就可以在这个钩子上设置一个定时器。this.timerID为定时器的ID,我们可以在componentWillUnmount()
钩子中卸载定时器。
代码执行顺序:
1.当
2.React然后调用Cl组件的render()方法。这是React了解屏幕上应该显示什么内容,然后React更新DOM以匹配Clock的渲染输出。
3.当Clock的输出插入到DOM中时,React调用comonentDidMount()生命周期钩子。在其中,Clock组件要求浏览器设置一个定时器,每秒钟调用一次tick().
4.浏览器每秒钟调用tick()方法,在其中,Clock组件使用包含当前时间的对象调用setState()来调用UI更新,通过调用setState(),React知道状态已经改变,并再次调用render()方法来确定屏幕上应当显示什么。这一次,render()方法中this.state.date将不同,所以渲染输出将包含更新的时间,并相应地更新DOM.
5.一旦Clock组件被从DOM中移除,React会调用componentWillUnmount()这个钩子函数,定时器也就会被清除。
数据自顶向下流动
父组件或子组件都不能知道某个组件是有状态和无状态,并且他们不应该关心某组件是被定义为一个函数还是一个类。
这就是为什么状态通常被成为局部或封装。除了拥有并设置它的组件外,其他组件不可访问。
以下实例中FormattedDate组件将在其属性中接受到date值,并且不知道它是来自Clock状态,还是来自Clock的属性,亦或手工输入:
1 | function FormattedDate(props){ |
通常被成为自顶向下或单向数据流,任何状态始终由某些特定组件所有,并且从该状态导出的任何数据或UI只能影响树中下方的组件。
如果你想象一个组件树作为属性的瀑布,每个组件的状态就像一个额外的水源,它连接在一个任意点,但也流下来。
为了表明所有组件都是真正隔离的,我们可以创建一个App组件,它渲染三个Clock;
1 | function FormattedDate(props){ |
以上实例中每个Clock组件都建立了自己的定时器并且独立更新。
在React应用程序中,组件是有状态还是无状态被认为是可能随时间而变化的组件的实现细节。
我们可以在有状态组件中使用无状态组件,也可以在无状态组件中使用有状态组件。
笔记
关于挂载时的setlnterval中调用tick()的方式()=>this.tick();
1.()=>this.tick()()=>this.tick()
是ES6中声明函数的一种方式,叫做箭头函数表达式,引入箭头函数有两个方面的作用:更简短的函数并且不绑定this.
1 | var f=([参数])=>表达式(单一) |
箭头函数的基本语法如下:
1 | (参数1,参数2,....,参数N) => {函数声明} |
根据以上概念,尝试将setlnterval中调用tick()的方式改成通常声明方式:
1 | this.timerID = setInterval(function(){ |
但是会报错,tick()不是一个方法
2.this.tick()
this.tick()中的this指代的时function,而不是我们想要的指代所在的组件类Clock,所以我们要想办法让this能被正常指代。我们这里采用围魏救赵的办法;
1 | let that = this; |
在闭包函数的外部先用that引用组件Clock中挂载组件方法componentDidMount()中this的值,然后再setlnterval中闭包函数中使用that,that无法找到声明,就会根据作用域链去上级(上次层)中继承that,也就是我们引用的组件类Clock中的this.
到此为止,将()=>this.tick()等价代换为了我们熟悉的形式。