一、prop和state
1、易于维护组件的设计要素
(1)高内聚:把逻辑紧密的相关内容放在一个组件中
(2)低耦合:不同组件之间的依赖关系尽量弱化
2、React组件的数据
分两种(都会引发组件的重新渲染):
prop:组件对外接口;(一般用在组件之间传参)
state:组件内部状态;(一般用在组件内部初始化)
3、React的prop:组件的prop很像是HTML元素的属性,但是HTML元素的属性值都是字符串,而React组件的prop所支持的类型几乎包含人一JS中所支持的类型
注意:
<SampleButton id=” sample” borderWidth={2} onClick={onButtonClick} style={{color: ” red”}} />
style值中的{{}} , 外层表示JSX语法,内层表示对象
(1)赋值
引入主入口组件App(另一个组件是第一章的demo)
向三个ControlPanel的子组件Counter进行赋值,使用了caption和initValue两个prop
(2)读取prop值
通过props 进行 读取值
结果显示:
(3)propType进行prop接口规范
此时对prop值进行了约束,
对caption必须是string,并且isRequired表示caption为必填值
对initValue必须为number
如果不按约束标准进行,如
则报错
注意:
即使在上面 propTypes 检查出错的情况下,组件依旧能工作 。 也就是说 propTypes检查只是一个辅助开发的功能,并不会改变组件的行为。
4、React的state:帮助记录组件自身的数据变化
注意:组件的state必须是JS对象,不能是其他数据类型
(1)初始化值
可以换一种更优雅的写法
一样可以实现默认值为0的效果
(2)读取和更新state
如果不使用setState(),修改代码如下
此时点击“+”,则会弹出警告
注意:
在不使用setState()情况下,当连续点击10次“+”,数值并没有发生改变
然后再次点击“-”时,数值直接变成9
可以看出在我们连续点击“+”,其实state值已经开始进行累加,但是没有渲染到页面上,当我们点击“-”时,使用setState(),将减去1之后的结果9显示在页面上
总结:直接使用state修改值,只修改了内部状态,并没有引起重新渲染
5、prop和state的对比
(1)prop定义外部接口,state记录内部状态
(2)prop的赋值在组件外部,state的赋值在组件内部
(3)组件不应该改变prop的值,而state的值允许被修改
(
组件不应该改变prop的值的原因:
假设一个场景,父组件包含多个子组件,然后将一个JS对象作为prop值传给这几个子组件,而某个子组件修改了这个对象的内部值,
那么其他子组件在读取那个对象的值时都会受到影响,导致程序混乱
书上没有实践案例,自己写个小demo测试一下(将之前代码都放demo1了,此次新创建demo2),如下:
首先,将父组件引入主入口文件
在Parent组件中,初始化content对象,并将它作为prop值,分别传入两个子组件
ChildOne组件对prop值进行直接修改,ChildTwo组件中对prop值进行显示,观察prop值是否变化
结果:
先点击ChildOne,然后点进ChilTwo进行显示,发现prop值在子组件Children发生改变,丢失了最初的值
)
二、组件的生命周期(v16.6.0)
截取react官网生命周期图
(1)装载过程(Mount):组件第一次在DOM树中渲染过程;
(2)更新过程(Update):当组件被重新渲染过程;
(3)卸载过程(Unmount):组件从DOM中删除的过程;
1、装载过程的对应函数:
(1)constructor:当创建组件类的实例时,会调用对应构造函数
注意:无状态的React组件,不需要定义构造函数
React组件定义构造函数的目的:
(i)初始化state
(ii)绑定成员函数的this环境
注意:
在ES6语法下,类的每个成员函数在执行时的this并不是和类实例自动绑定的,而在构造函数中,this就是当前组件的实例。
所以,为了方便将来的调用,在构造函数中将这个实例的特定函数绑定this为当前实例。
如:
this .onClickincrementButton = this .onClickincrementButton.bind(this);
(2)render:React组件中最重要的函数,一个React组件可以不实现其他所有函数,但是一定要实现render函数
(i)某些特殊组件不是用来渲染界面,就让render函数返回一个null或者false,等于告诉React,这个组件不需要渲染DOM元素
(ii)render函数是一个纯函数,根据this.state和this.props决定返回结果,不能在里面进行this.setState改变状态
(3)componentWillMount和componentDidMount:
(i)componentWillMount:这个函数在渲染前被调用,几乎不会被使用到,因为所有可以在这个 componentWillMount 中做的事情,都可以提前到 constructor 中间去做
(ii)componentDidMount:这个函数被调用时,组件已经被装载到DOM树上了
注意:
执行顺序,结果:
可以看出componentWillMount紧贴着自己组件的render函数之前被调用,componentDidMount在三个组件的render函数都被调用之后,
三个组件的componentDidMount才连着被一起调用。
原因:
是因为 render 函数本身并不往 DOM 树上渲染或者装载内容,它只是返回一个 JSX表示的对象,然后由 React库来根据返回对象决定如何渲染。而 React库肯定是要把所有组件返回的结果综合起来,才能知道该如何产生对应的 DOM修改。 所以,只有 React库调用三个 Counter组件的 render函数之后,才有可能完成装载,这时候才会依次调用各个组件的 componentDidMount 函数作为装载过程的收尾 。
(iii)componentWillMount可以在服务器端或浏览器端被调用,componentDidMount只能在浏览器端被调用
(iv)在 componentDidMount 被调用的时候,组件已经被装载到 DOM 树上了,可以配合其他的第三方UI库(如:jQuery),进行添加新的功能
2、更新过程的对应函数:
(1)componentWillReceiveProps(nextProps)
(i)通过 this.setState方法触发的更新过程不会调用这个函数;
(ii)只要是父组件的render函数被调用,在render函数里面被渲染的子组件就会经历更新过程,
不管父组件传给子组件的props有没有改变,都会触发子组件的componentWillReceiveProps函数
根据如下例子证明:
父组件
当触发onClick事件时,引发一个匿名函数,通过forceUpdate进行重新绘制(每个React组件都可以通过forceUpdate函数强行进行重新绘制)
子组件
运行结果
显示页面初始化的数据,当进行点击Click me to repaint!时
此时可以看出:
执行次序是当父组件的render被调用后,引发了子组件中componentWillReceiveProps的调用,最后子组件中的render也被调用了;
再次渲染子组件时,它们的props并没有变化,但是子组件中componentWillReceiveProps依然被调用了 (有需要在子组件对nextProps和this.props值进行比较)
再验证一下this.setState能不能触发componentWillReceiveProps
此时可以看出:
当前组件调用this.setState函数,不会引发componentWillReceiveProps函数
(2)shouldComponentUpdate(nextProps,nexState):除了render之外最重要的函数,通过返回的布尔值决定什么时候组件不需要渲染,
(i)如果我们要定义 shouldComponentUpdate,那就根据这两个参数,外加 this.props和 this.state来判断是返回true,还是false,来避免重复渲染
(ii)通过this.setState函数引发更新过程,并不是立刻更新组件的state值,在执行到函数shouldComponentUpdate的时候,this.state依然是this.setState函数执行之前的值
如:
运行结果:
可以看出,三个Counter组件的render函数没有被调用,因为这个刷新没有改变caption值或count,减少了不必要的渲染
(3)componentWillUpdate和componentDidUpdate
(i) componentDidUpdate函数,不仅在浏览器端可以执行,服务器端也可以执行
(ii)在配合其他第三方UI库时(如:jQuery),当React组件被更新时,原有内容会被重新绘制,这时候需要在componentDidUpdate函数再次调用jQuery代码
3、卸载过程的对应函数:
(1)componentWillUnmount:当React组件要从DOM树上删除之前,此函数会被调用,适合做一些清除工作(如:防止内存泄漏)
三、组件向外传递数据
通过onUpdate这个prop值为onCounterUpdate函数,作为“桥梁”,通过函数的参数进行“沟通”