八、react生命周期函数
- 函数组件无生命周期一说
- 生命周期函数指在某一时刻组件会自动调用并执行的函数
常见生命周期图
一、新旧生命周期函数
1、旧生命周期函数
- 初始化阶段:由ReactDOM.render()触发---初次渲染
- constructor()
- componentWillMount()
- render()
- componentDidMount()
- 更新阶段:由组件内部this.setState()或父组件重新render触发
- shouldComponentUpdate()
- componentWillUpdate()
- render()
- componentDidUpdate()
- 卸载:由ReactDOM.unmountComponentAtNode(组件)触发
- componentWillUnmount()
2、新生命周期函数
- 初始化阶段:由ReactDOM.render()触发---初次渲染
- constructor()
- getDerivedStateFromProps()
- render()
- componentDidMount()
- 更新阶段:由组件内部this.setState()或父组件重新render触发
- getDerivedStateFromProps()
- shouldComponentUpdate()
- render()
- getSnapshortBeforeUpdate()
- componentDidUpdate()
- 卸载:由ReactDOM.unmountComponentAtNode(组件)触发
- componentWillUnmount()
3、重要的钩子函数
- render():初始化渲染或更新时调用
- componentDidMount():开启监听、发送ajax请求
- componentWillUnmount():做一些收尾工作
4、
1 // 类似于vue里的data 2 data(): { 3 return { 4 a: 0 5 } 6 }, 7 props: ["num"], 8 mounted() { 9 this.a = this.num; 10 }
上述vue代码转化成当前的react代码即有如下的效果:
1 state = { 2 num: 0, 3 uname: "zhangsan", 4 }; 5 6 render() { 7 return <div>当前的num是{this.state.num}</div>; 8 } 9 10 // 从props中获取数据,绑定到当前的这个组件中的state 11 static getDerivedStateFromProps(props, state) { 12 if (props.amount === state.num) { 13 // 不需要更新当前state 14 return null; 15 } else { 16 return { num: props.amount }; 17 } 18 }
如果本身当前组件中的状态数据有很多,但是生命周期函数最后返回的只是其中的部分数据,这个不会覆盖掉其他的数据。
例如上述代码,对象虽然只返回了num值,结果不会使uname丢失。
-
-
如果是由于父组件的
props
更改,所带来的重新渲染,也会触发此方法 -
调用
setState()
不会触发getDerivedStateFromProps()
-
之前是使用
constructor
+componentWillRecieveProps
完成相同的功能 -
-
-
默认每个状态的更改都会重新渲染,大多数情况下应该保持这个默认行为
-
在渲染新的
props
或state
前,shouldComponentUpdate
会被调用 -
这个方法不会在初始化时被调用,也不会在
forceUpdate()
时被调用 -
返回
false
不会阻止子组件在state
1 shouldComponentUpdate(nextProps, nextState) { 2 // 判断是否需要被渲染,如果需要则返回true,否则返回false 3 // nextProps.b最新的b的值,this.props.b是原先的b的值 4 // 比较的时候需要使用全等运算符 5 if (nextProps.b === this.props.b) { 6 return false; 7 } else { 8 return true; 9 } 10 }
1 // 上述代码虽然可以解决组件不用被无缘无故的渲染浪费资源,但是如果涉及到的条件值较多的话则编写起来是一件非常痛苦的事情 2 // 因此React给我们提供简化的写法,使用简化写法后,这个生命周期函数就忘了它吧 3 // 简化方法:只需要将类组件的继承关系改成继承`PureComponent`即可,这样一来,框架会自动判断值是否有变化进一步决定组件是否需要被渲染 4 import React, { PureComponent } from "react"; 5 6 class Cmp6 extends PureComponent { 7 render() { 8 console.log("Cmp6被渲染了"); 9 return <div>父亲传递过来的值b为:{this.props.b}</div>; 10 } 11 } 12 export default Cmp6;
如果shouldComponentUpdate()
返回false
,componentWillUpdate
,render
和componentDidUpdate
不会被调用
3、render
5、
1 import React from 'react' 2 import './newslist.css' 3 4 class NewsList extends React.Component{ 5 state = {newsArr: 0} 6 componentDidMount(){ 7 setInterval(() => { 8 const {newsArr} = this.state 9 const news = '新闻' + {newsArr.length + 1} 10 this.setState(() => { 11 return {newsArr: [news, ...newsArr]} 12 }) 13 }) 14 } 15 getSnapshotBeforUpdate(){ 16 return this.refs.list.scrollHeight 17 } 18 componentDidUpdate(prevProps, prevState, height){ 19 // this.refs.list.scrollHeight和height的差值是一定的(比如11条的高度和10条的高度) 20 this.refs.list.scrollTop += this.refs.list.scrollHeight - height 21 } 22 render(){ 23 return( 24 <div className="list" ref="list"> 25 { 26 this.state.newsArr.map((n, index) => { 27 return <div className="news" key={index}>{n}</div> 28 }) 29 } 30 </div> 31 ) 32 } 33 } 34 export defalult NewsList;
1 /*newslist.css*/ 2 .list{ 3 width: 200px; 4 height: 150px; 5 background: skyblue; 6 overflow: auto; 7 } 8 .news{ 9 height: 30px; 10 }
四、卸载组件
由ReactDOM.unmountComponentAtNode(组件)触发,对应ReactDOM.render
1、componentWillUnmount()
- 类似于vue的beforeDestory,在组件被卸载并销毁之前立即被调用
五、其他
捕获生命周期里的错误(主要是render里)
子组件有错误,在父组件里处理,使错误不影响外层功能
父组件
1 import React,{Component} from 'react' 2 import Child from './Child' 3 4 export default class Parent extends Component{ 5 state = { 6 hasError: '' // 用于标识子组件是否产生错误 7 } 8 static getDerivedStateFromError(error){ 9 console.log('错误信息',error) 10 return {hasError: error} 11 } 12 componentDidCatch(){ 13 console.log('此处统计错误次数,发送给后台') 14 } 15 render(){ 16 return( 17 <div> 18 <h2>我是Parent组件</h2> 19 {this.state.hasError ? <h2>当前网络不稳定</h2> : <Child/>} 20 </div> 21 ) 22 } 23 }
子组件
1 import React,{Component} from 'react' 2 3 export default class Parent extends Component{ 4 state = { 5 users: 'abc' 6 } 7 render(){ 8 return( 9 <div> 10 <h2>我是Child组件</h2> 11 { 12 // users是数组才能map 13 this.state.users.map((userObj) => { 14 return <h4 key={userObj.id}>:{userObj.name}</h4> 15 }) 16 } 17 </div> 18 ) 19 } 20 }
2、static getDerivedStateFromError()
1 import React from 'react' 2 class Demo extends React.Component{ 3 state = { 4 persons: [ 5 {id: 1, name: '张三', age: 18} 6 ] 7 } 8 add = () => { 9 const {persons} = this.state 10 const newPerson = {id: persons.length + 1, name: '王五', age: 20} 11 this.setState(state => { 12 return { 13 // newPerson加在数组前面 14 persons: [newPerson, ...persons] 15 } 16 }) 17 } 18 render(){ 19 return( 20 <div> 21 <ul> 22 { 23 this.state.persons.map((item, index) => { 24 return( 25 <li key={index}> 26 {item.name}---{item.age} 27 </li> 28 ) 29 }) 30 } 31 </ul> 32 </div> 33 ) 34 } 35 }
更新前后对比
1 //初始数据 2 {id: 1, name: '张三', age: 18} 3 {id: 2, name: '李四', age: 19} 4 //初始虚拟DOM 5 <li key=0>张三---18</li> 6 <li key=1>李四---19</li> 7 8 //更新后数据 9 {id: 3, name: '王五', age: 20} 10 {id: 1, name: '张三', age: 18} 11 {id: 2, name: '李四', age: 19} 12 //更新后的虚拟DOM,key更改了,不能用以前的虚拟DOM,效率降低 13 <li key=0>王五---20</li> 14 <li key=1>张三---18</li> 15 <li key=2>李四---19</li>