11.react生命周期

1. React父子组件通信

1.1 父亲给孩子传递参数

1
2
3
4
return (<div>
App
<CMTItem name={'zhangsan'} {...obj}></CMTItem>
</div>)

1.2 孩子给父亲传递参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
> // 父亲中用来接收参数的函数 
> getChild = (obj) => {
> const id = this.state.CommentList.length + 1;
> console.log()
> obj.id = id;
> console.log("父亲接收数据的方法",obj);
> this.setState((state) =>({
> CommentList :[obj,...this.state.CommentList] ★★
> // 用来添加到数组,
> }))
> }
> // 父亲中的子组件的使用
> return (<div>
> App
> <CMTItem func={reload}></CMTItem>
> </div>)
>
> // 按钮绑定的函数用来传递数据
> postComment = () => {
> var obj = {}
> obj.user = this.refs.user.value
> obj.content = this.refs.content.value
> console.log(obj)
> if(this.refs.user.value&& this.refs.content.value){
> this.props.func(obj); ★
> }
> this.refs.user.value = this.refs.content.value =""
> }
>

1.3 Context的跨层传递

  • 使用要点:1.保证父子组件必须是类组件 ★
1
2
3
4
5
6
//这个方法用于给子孙组件传递数据 (数据的跨层传递)
getChildContext() {
return {
color: this.state.color
}
}
  • 定义向子孙组件传递的数据的类型
1
2
3
static childContextTypes = {
color: PropTypes.string
}
  • 子组件在使用父组件context数据的时候首先需要对父组件传递过来的数据做类型校验
1
2
3
4
render(){
//从this.context中获取跨层传递的数据
return (<div>这是CMTChild组件 {this.context.color} </div>)
}

2. react 组件的生命周期

2.1 对比 vue 的生命周期

1
2
3
4
5
6
7
8
9
Vue生命周期:
1. beforeCreate 组件创建之前,此时method和state还不可以使用
2. created 组件创建完毕,此时method和state可以使用
3. beforeMount 在内存中创建虚拟dom,此时页面还没有挂载
4. mounted 内存中虚拟dom已经渲染到页面,此时页面是最新的
5. beforeUpdate 页面更新之前,此时页面还是旧的
6. updated 页面更新之后,此时页面是最新的
7. beforeDestory 组件销毁之前
8. destroyed 组件销毁之后

2.2 react 生命周期的介绍

  • React组件的生命周期分为三个阶段:组件的创建阶段、组件的运行阶段、组件的销毁阶段
a. 创建阶段
1. static defaultProps
1
2
3
4
5
// 在封装一个组件的时候,组件内部肯定有一些数据是必须的,哪怕用户没有传递一些相关的启动参数,这时候组件内部尽量给自己提供一个默认值;
// 在 React 中使用静态的defaultProps属性来设置组件的默认属性值;
static defaultProps = {
initcount: 0 // 如果外界没有传递 initcount 那么自己初始化一个 数值为0
}
1
2
3
4
5
6
7
8
 import ReactTypes from "prop-types"
// 这是创建一个 静态的 propTypes 对象,可以对外界传递过来的属性做类型校验;
// 注意: 如果要为传递过来的属性做类型校验,必须安装 React 提供的 第三方包叫做 prop-types ;
static propTypes = {
// 使用 prop-types 包 来定义 initcount 为 number 类型
// isRequired 表示这个props属性是必须要传递的
initcount: ReactTypes.number.isRequired
}
  • 如果有些数据是必须的,必须要有初始值,但是没有传递,就可以使用 static
2.constructor
1
2
3
4
5
6
7
8
9
10
11
//2.生命周期第二个执行的是constructor 构造函数 (执行一次)
// 在创建组件对象的时候会自动调用构造函数
constructor(props) {
console.log("constructor")
super(props)
//在构造函数中指定组件的私有数据
this.state = {
count: props.initcount
}
this.divRef = React.createRef()
}
3. componentWillMount
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//3.当子组件接收到新的props会执行,在react16.4版本以后,setState修改state数据之后也会触发这个方法(执行多次)
// 在该方法中不能通过this.setSate来修改state数据,因为在staic修饰的方法中就没有this
//作用:将传递的props映射到state里面
//参数: nextProps 外部传递过来的props属性
// prevState 之前的state状态
static getDerivedStateFromProps(nextProps, prevState) {
console.log("getDerivedStateFromProps", nextProps, prevState)
//获取到父组件中传递过来的initcount值
const { initcount } = nextProps;
//当父组件中传递过来的initcount发生改变了
if (initcount != prevState.prevCount) {
//在方法中返回{count:initcount} 替换掉当前state中的 count值
return {
count: initcount,
prevCount:initcount
}
}
//如果父组件中传递过来的initcount没有变化,就返回一个空对象(此时不对当前的state做任何修改)
return null
}
4.render (){}
1
2
3
4
5
6
7
8
//4.创建虚拟dom,但是虚拟dom还没有挂载到页面 (执行多次)
// render函数中不能使用setState()来修改state的数据,因为会死循环
render() {
console.log("render", this.divRef.current) //null
return (<div ref={this.divRef}>
哈哈Counter {this.state.count}
</div>)
}
5.componentDidMount

虚拟 dom 已经挂载到页面,此时可以获取到最新的页面(执行一次)

1
2
3
4
//5.虚拟dom已经挂载到页面,此时可以获取到最新的页面 (执行一次)
componentDidMount() {
console.log("componentDidMount", this.divRef.current, this.state) //null
}
b. 运行阶段
1.static getDerivedStateFromProps
1
2
3
第一个会执行:static getDerivedStateFromProps
当父组件给子组件传递的props发生变化的时候,会执行该方法。
在react16.4版本以后,当前组件的state发生变化,会触发forceUpdate方法,然后还是会触发该方法
2.shouldComponentUpdate
1
2
3
4
5
6
7
8
9
10
// 第二个会执行: shouldComponentUpdate
// 在这个函数中,我们可以设置指定条件下去更新页面,指定条件下不更新页面。这个函数中入参props和state都是最新的
// 在该方法中不能通过this.setSate来修改state数据
shouldComponentUpdate(nextProps, nextState) {
console.log("shouldComponentUpdate", nextProps, nextState)
// 在 shouldComponentUpdate 中要求必须返回一个布尔值
// 在 shouldComponentUpdate 中,如果返回的值是 false,则不会继续执行后续的生命周期函数,而是直接退回到了运行中的状态,此时后续的render函数并没有被调用,因此页面不会被更新,但是组件的 state 状态,却被修改了;
// return nextState.count % 2 == 0 ? true : false;
return true;
}
3.render
1
//第三个会执行:render  此时会重新构建虚拟dom,但是新的虚拟dom还没有渲染到页面
4.getSnapshotBeforeUpdate
1
2
3
4
5
6
//第四个会执行getSnapshotBeforeUpdate 函数
// 在该方法中不能通过this.setSate来修改state数据
getSnapshotBeforeUpdate() {
console.log("getSnapshotBeforeUpdate")
return {}
}
5.componentDidUpdate ★★
1
2
3
4
5
6
  // 在该方法中不能通过this.setSate来修改state数据
//表示组件已经更新完毕 此时可以获取到最新的页面
componentDidUpdate() {
console.log("componentDidUpdate", this.state)
}
//只有在 componentDidMount 这个生命周期钩子函数中才可以写 setState, 其他钩子函数统统不可以写 ★
c . 销毁阶段
1. componentWillUnmount()
1
2
3
4
5
6
7
8
//很关键的,我们获取当前rootNode的scrollHeight,传到componentDidUpdate 的参数perScrollHeight
//在render方法调用之后,在componentDidUpdate之前执行,我们可以在这个方法中获取到元素的滚动位置的信息
//还可以在该方法中做一些样式的微调
getSnapshotBeforeUpdate() {
//console.log(this.rootNode.clientHeight)
//this.rootNode.style.backgroundColor = "red";
return this.rootNode.scrollHeight;
}