React生命周期

17次阅读

共计 1373 个字符,预计需要花费 4 分钟才能阅读完成。

React 类似 VueJs 一样具有自己的生命周期,它们的生命周期都会经历三个阶段:挂载、更新、卸载,而每一个阶段都有其对应的不同的钩子函数,只有充分了解其生命周期,在进行编码的过程中才能在不同的钩子函数中做我们自己想做的事情

图片来源:https://github.com/wojtekmaj/…
使用类组件而不是常规函数的一个优点是能够使用特殊的 React 方法或钩子来允许我们更新 UI 和应用程序状态。了解何时使用每种方法对于按照我们所希望的方式加载应用程序非常重要。
我们将针对 React 生命周期的三个阶段进行讨论:

  • 挂载时(Mounting)
  • 更新时(Updating)
  • 卸载时(UnMounting)

挂载时

当创建一个组建的时候,它会挂载到 DOM 上。

constructor()

  • 当页面加载的时候,最初也是唯一一次被调用
  • 用于做一些初始化状态的操作
  • 唯一可以修改 state 的地方(因为 react 一般想要修改 state,一般会调用 setState 方法)

static getDerivedStateFromProps(nextProps, prevState)

  • 当 state 需要从 props 初始化时使用
  • 每次 render 或者 rerender 的时候都会调用
  • 尽量不要使用:维护两者一致性会增加复杂度
  • 典型场景:表单控件获取默认值

render()

  • 这是类组件中唯一必需的方法
  • 每次 React 更新并提交到 DOM 的时候调用
  • 用于为组件编写 JSX

componentDidMount()

  • 在构造组件并将其添加到 DOM 上 (渲染后) 只执行一次
  • 可用于获取数据并在渲染完成后立即显示
  • 典型场景:获取外部资源

更新时

每当一个组件的 state 和 props 改变的时候,将在页面上重新进行渲染

static getDerivedStateFromProps(nextProps, prevState)

  • 每次页面 render 之前调用,state 已更新
  • 典型场景:获取 render 之前的 DOM 状态
  • 很少使用:将 props 复制到 state

shouldComponentUpdate(nextProps, nextState)

  • 返回一个布尔类型的值,默认返回 true
  • 在渲染 (render) 之前或组件接受到新的 state 和 props 的时候马上执行
  • 在每次重新渲染之前调用,但不是初始化操作时调用
  • 决定 Virtual Dom 是否要重绘
  • 对于性能优化,可以做一些不必重新渲染的操作

render()

  • 对于一个类组件,这是唯一必需的方法
  • 对于更新,如果 shouldComponentUpdate()返回 false,则不会调用 render()。

getSnapshotBeforeUpdate(prevProps, prevState)

  • 此方法允许我们捕获在呈现该组件之前未存储在该状态中的某些属性。(即,如果用户滚动到页面上的特定位置,并且我们想要存储该位置并在以后使用它)
  • 在 React 更新并提交新的内容到 DOM 之前调用
  • 很少使用但可以捕获可能快速变化的数据

componentDidUpdate(previousProps, previousState, snapshot)

  • 在组件已经重新渲染之后调用
  • 用于渲染后的任何 DOM 更新

卸载时

在此阶段,组件将被删除并从 DOM 中清除

componentWillUnmount

  • 在从 DOM 卸载组件之前调用它,做一些回收类的操作,如清除定时器等
正文完
 0

react 生命周期

17次阅读

共计 7611 个字符,预计需要花费 20 分钟才能阅读完成。

一个 React 组件的生命周期分为三个部分:实例化、存在期和销毁时。
实例化阶段

客户端渲染时,如下依次被调用

getDefaultProps()
getInitialState()
componentWillMount()
render()
componentDidMount()

服务端渲染

getDefaultProps()
getInitialState()
componentWillMount()
render()

注意:componentDidMount() 不会再服务端被渲染;
getDefaultProps

对于每个组件实例来讲,这个方法只会调用一次,该组件类的所有后续应用,getDefaultPops 将不会再被调用,其返回的对象可以用于设置默认的 props 值。
var Hello = React.creatClass({
getDefaultProps: function(){
return {
name: ‘pomy’,
git: ‘dwqs’
}
},

render: function(){
return (
<div>Hello,{this.props.name},git username is {this.props.dwqs}</div>
)
}
});

ReactDOM.render(<Hello />, document.body);

也可以在挂载组件的时候设置 props。
var data = [{title: ‘Hello’}];
<Hello data={data} />

或者调用 setProps(一般不需要调用)来设置其 props
var data = [{title: ‘Hello’}];
var Hello = React.render(<Demo />, document.body);
Hello.setProps({data:data});
但只能在子组件或组件树上调用 setProps。别调用 this.setProps 或者 直接修改 this.props。将其当做只读数据。
React 通过 propTypes 提供了一种验证 props 的方式,propTypes 是一个配置对象,用于定义属性类型:
var survey = React.createClass({
propTypes: {
survey: React.PropTypes.shape({
id: React.PropTypes.number.isRequired
}).isRequired,
onClick: React.PropTypes.func,
name: React.PropTypes.string,
score: React.PropTypes.array

},

//…
})
或者
import React, {Component} from ‘react’
import PropTypes from ‘prop-types’

class BetterImage extends Component{…}

BetterImage.PropTypes={
src: PropTypes.string,
center: PropTypes.bool,
loadingImage: PropTypes.string,
defaultImage: PropTypes.string,
onLoad: PropTypes.func,
onError: PropTypes.func,
onComplete: PropTypes.func
}
BetterImage.defaultProps={
….
}

getInitialState

对于组件的每个实例来说,这个方法的调用有且只有一次,用来初始化每个实例的 state,在这个方法里,可以访问组件的 props。每一个 React 组件都有自己的 state,其与 props 的区别在于 state 只存在组件的内部,props 在所有实例中共享。
getInitialState 和 getDefaultPops 的调用是有区别的,getDefaultPops 是对于组件类来说只调用一次,后续该类的应用都不会被调用,而 getInitialState 是对于每个组件实例来讲都会调用,并且只调一次。
var LikeButton = React.createClass({
// 初始化 State
getInitialState: function() {
return {liked: false};
},

handleClick: function(event) {
// 设置修改 State
this.setState({liked: !this.state.liked});
},

render: function() {
var text = this.state.liked ? ‘like’ : ‘haven\’t liked’;
return (
<p onClick={this.handleClick}>
You {text} this. Click to toggle.
</p>
);
}
});

ReactDOM.render(
<LikeButton />,
document.getElementById(‘example’)
);

每次修改 state,都会重新渲染组件,实例化后通过 state 更新组件,会依次调用下列方法:
1、shouldComponentUpdate
2、componentWillUpdate
3、render
4、componentDidUpdate

componentWillMount
在渲染前调用, 在客户端也在服务端。React 官方正式发布了 v16.3 版本。在这次的更新中,除了前段时间被热烈讨论的新 Context API 之外,新引入的两个生命周期函数 getDerivedStateFromProps,getSnapshotBeforeUpdate 以及在未来 v17.0 版本中即将被移除的三个生命周期函数 componentWillMount,componentWillReceiveProps,componentWillUpdate .
在这个生命周期中你会遇到一下问题:
a. 首屏无数据导致白屏 在 React 应用中,许多开发者为了避免第一次渲染时页面因为没有获取到异步数据导致的白屏,而将数据请求部分的代码放在了 componentWillMount 中,希望可以避免白屏并提早异步请求的发送时间。但事实上在 componentWillMount 执行后,第一次渲染就已经开始了,所以如果在 componentWillMount 执行时还没有获取到异步数据的话,页面首次渲染时也仍然会处于没有异步数据的状态。换句话说,组件在首次渲染时总是会处于没有异步数据的状态,所以不论在哪里发送数据请求,都无法直接解决这一问题。而关于提早发送数据请求,官方也鼓励将数据请求部分的代码放在组件的 constructor 中,而不是 componentWillMount。若是为了改善用户体验曾经用过的解决方法有两个:
方法一:异步请求组件,使用 nprogress 添加加载动画;
import React, {Component} from ‘react’
import NProgress from ‘nprogress’
import ‘nprogress/nprogress.css’
import ‘./customNprogress.styl’

NProgress.configure({showSpinner: false})

export default function asyncComponent(importComponent) {
class AsyncComponent extends Component {
state = {
component: null
}

async componentDidMount() {
NProgress.start()
const {default: component} = await importComponent()
NProgress.done()

this.setState({component})
}

render() {
const C = this.state.component

return C ? <C {…this.props} /> : null
}
}

return AsyncComponent
}

const AsyncNotFound = asyncComponent(() => import(/* webpackChunkName: “NotFound” */ ‘@/routes/NotFound’))

方法二:使用 onreadystatechange 去监听 readyState,在资源加载完成之前加载一个只有框架的静态页面,页面不请求数据。当数据请求完成之后再将路由切换到真实的首页。
function listen () {
if (document.readyState == ‘complete’) {// 资源加载完成
ReactDom.render(
<Provider store={store}>
<Router>
<Route path=”/” component={Index}/>
</Router>
</Provider>,
document.getElementById(‘root’)
)
} else {// 资源加载中
ReactDom.render(
<Provider store={store}>
<Router>
<Route path=”/” component={FirstScreen}/>
</Router>
</Provider>,
document.getElementById(‘root’)
)
}
}

document.onreadystatechange = listen

具体参考解决 React 首屏加载白屏的问题
b. 事件订阅
另一个常见的用例是在 componentWillMount 中订阅事件,并在 componentWillUnmount 中取消掉相应的事件订阅。但事实上 React 并不能够保证在 componentWillMount 被调用后,同一组件的 componentWillUnmount 也一定会被调用。一个当前版本的例子如服务端渲染时,componentWillUnmount 是不会在服务端被调用的,所以在 componentWillMount 中订阅事件就会直接导致服务端的内存泄漏。另一方面,在未来 React 开启异步渲染模式后,在 componentWillMount 被调用之后,组件的渲染也很有可能会被其他的事务所打断,导致 componentWillUnmount 不会被调用。而 **componentDidMount 就不存在这个问题,在 componentDidMount 被调用后,componentWillUnmount 一定会随后被调用到,并根据具体代码清除掉组件中存在的事件订阅。**

render

该方法会创建一个虚拟 DOM,用来表示组件的输出。对于一个组件来讲,render 方法是唯一一个必需的方法。render 方法需要满足下面几点:

只能通过 this.props 和 this.state 访问数据(不能修改)
可以返回 null,false(这种场景下,react 渲染一个 <noscript> 标签,当返回 null 或者 false 时,ReactDOM.findDOMNode(this) 返回 null) 或者任何 React 组件
只能出现一个顶级组件,不能返回一组元素
不能改变组件的状态
不能修改 DOM 的输出

render 方法返回的结果并不是真正的 DOM 元素,而是一个虚拟的表现,类似于一个 DOM tree 的结构的对象。react 之所以效率高,就是这个原因。
render 执行情况如下:
1. 首次加载
2. setState 改变组件内部 state。
注意:此处是说通过 setState 方法改变。
3. 接受到新的 props

注意: 因为数据是异步的情况,会导致组件重复渲染
componentDidMount

该方法不会在服务端被渲染的过程中调用。该方法被调用时,已经渲染出真实的 DOM,可以再该方法中通过 this.getDOMNode() 访问到真实的 DOM( 推荐使用 ReactDOM.findDOMNode())。
var data = [..];
var comp = React.createClass({
render: function(){
return <imput .. />
},
componentDidMount: function(){
$(this.getDOMNode()).autoComplete({
src: data
})
}
})
由于组件并不是真实的 DOM 节点,而是存在于内存之中的一种数据结构,叫做虚拟 DOM(virtual DOM)。只有当它插入文档以后,才会变成真实的 DOM。有时需要从组件获取真实 DOM 的节点,这时就要用到 ref 属性:
var Area = React.createClass({
render: function(){
this.getDOMNode(); //render 调用时,组件未挂载,这里将报错

return <canvas ref=’mainCanvas’>
},
componentDidMount: function(){
var canvas = this.refs.mainCanvas.getDOMNode();
// 这是有效的,可以访问到 Canvas 节点
}
})
需要注意的是,由于 this.refs.[refName] 属性获取的是真实 DOM,所以必须等到虚拟 DOM 插入文档以后,才能使用这个属性,否则会报错。如果 ref 回调函数以 inline 函数的方式来指定,那么在组件更新的时候 ref 回调会被调用 2 次。第一次回调的时候传入的参数是 null, 而第二次的时候才真正的传入 DOM 节点
更多了解 ref 使用从 React 官方文档看 refs 的使用和未来获取真实 dom,并获取 dom css 三种方法
存在期
此时组件已经渲染好并且用户可以与它进行交互,比如鼠标点击,手指点按,或者其它的一些事件,导致应用状态的改变,你将会看到下面的方法依次被调用;

componentWillReceiveProps()
shouldComponentUpdate()
componentWillUpdate()
render()
componentDidUpdate()

componentWillReceiveProps

当 props 发生变化时执行,初始化 render 时不执行,在这个回调函数里面,你可以根据属性的变化,通过调用 this.setState() 来更新你的组件状态,旧的属性还是可以通过 this.props 来获取, 这里调用更新状态是安全的,并不会触发额外的 render 调用。
componentWillReceiveProps: function(nextProps){
if(nextProps.checked !== undefined){
this.setState({
checked: nextProps.checked
})
}
}
了解更多点击此处
shouldComponentUpdate

shouldComponentUpdate 函数是重渲染时 render() 函数调用前被调用的函数,它接受两个参数:nextProps 和 nextState,分别表示下一个 props 和下一个 state 的值。并且,当函数返回 false 时候,阻止接下来的 render() 函数及后面的 componentWillUpdate,componentDidUpdate 方法的调用,阻止组件重渲染,而返回 true 时,组件照常重渲染。
了解更多点击此处 – 真的讲的好
componentWillUpdate

这个方法和 componentWillMount 类似,在组件接收到了新的 props 或者 state 即将进行重新渲染前,componentWillUpdate(object nextProps, object nextState) 会被调用,注意不要在此方面里再去更新 props 或者 state。
componentDidUpdate

这个方法和 componentDidMount 类似,在组件重新被渲染之后,componentDidUpdate(object prevProps, object prevState) 会被调用。可以在这里访问并修改 DOM。
销毁
componentWillUnmount

每当 React 使用完一个组件,这个组件必须从 DOM 中卸载后被销毁,此时 componentWillUnmout 会被执行,完成所有的清理和销毁工作,在 componentDidMount 中添加的任务都需要再该方法中撤销,如创建的定时器或事件监听器。
当再次装载组件时,以下方法会被依次调用:
1、getInitialState2、componentWillMount3、render4、componentDidMount

React v.16 生命周期
constructor(props) // 初始化参数

componentWillMount()

render() // 第一次渲染

componentDidMount()

** 当父组件向子组件传入 props 发生改变后,依次调用 **

componentWillReceiveProps(nextProps)

shouldComponentUpdate(nextProps, nextState)

componentWillUpdate()

render() // 子组件更新渲染

componentDidUpdate()

** 当组件自身 state 发生变化后 **

componentWillUpdate()

render() // 组件再次更新渲染

componentDidUpdate()

当组件卸载

componentWillUnmount()

与低于 React16 版本的比较

React16 新的生命周期弃用了 componentWillMount、componentWillReceiveProps,componentWillUpdate
新增了 getDerivedStateFromProps、getSnapshotBeforeUpdate 来代替弃用的三个钩子函数(componentWillMount、componentWillReceivePorps,componentWillUpdate)
React16 并没有删除这三个钩子函数,但是不能和新增的钩子函数(getDerivedStateFromProps、getSnapshotBeforeUpdate)混用,React17 将会删除 componentWillMount、componentWillReceivePorps,componentWillUpdate
新增了对错误的处理(componentDidCatch)

相关文章
那个生命周期方法更适合请求数据 react 服务端渲染来谈谈 Reactv16.3 新生命周期知识点及遇到的问题 React16 版生命周期

正文完
 0

React生命周期

17次阅读

共计 105 个字符,预计需要花费 1 分钟才能阅读完成。

装载过程
当组件第一次被渲染时,依次调用的函数:
constructiongetInitalStategetDefaultPropscomponentWillMountrendercomponentDidMount

正文完
 0

react生命周期

17次阅读

共计 2939 个字符,预计需要花费 8 分钟才能阅读完成。

参考资料:React:组件的生命周期 这个文档利用 es5 的 creatClass,有些陈旧。React 生命周期 这个过于粗暴,没有示例
一个 React 组件的生命周期分为三个部分:实例化、存在期和销毁时,如下图:

实例化

getDefaultProps() 设置默认的 props,也可以用 dufaultProps 设置组件的默认属性.
getInitialState() 在使用 es6 的 class 语法时是没有这个钩子函数的,可以直接在 constructor 中定义 this.state。每一个 React 组件都有自己的 state,其与 props 的区别在于 state 只存在组件的内部,props 在所有实例中共享。
componentWillMount() 组件初始化时只调用,以后组件更新不调用,整个生命周期只调用一次,此时可以修改 state。

render()react 最重要的步骤,创建虚拟 dom,进行 diff 算法,更新 dom 树都在此进行。此时就不能更改 state 了。只能通过 this.props 和 this.state 访问数据(不能修改),也就是不能调用 this.setState()。只能出现一个顶级组件,不能返回一组元素。例如:
render(){
// 这样不行
this.setState({
newState:_newState
});
return (
<div></div>
// 去掉下面一行
<div></div>
);
}

componentDidMount() 组件渲染之后调用,只调用一次。

demo
import React,{Component} from ‘react’;
import ReactDOM from ‘react-dom’;
class Clock extends Component{

constructor(props){
super(props);
this.state = {
date:new Date()
}
}

// 渲染前 render 执行
componentWillMount(){
//this.timer///////////////////////////
this.timer =setInterval(()=>{
this.setState({
date: new Date()
})
},1000)
}
componentDidMount(){
this.clock.focus();
}
componentWillUnmount(){
clearInterval(this.timer)
}
render(){
return(
<div>
<h1>
<p ref={(clock)=>this.clock=clock}>now time:</p>
{this.state.date.toLocaleTimeString()}
</h1>
</div>
)
}
}
ReactDOM.render(<Clock />, document.getElementById(‘root’));
存在期,更新操作改变

componentWillReceiveProps(nextProps) 只有 props 改变时调用
shouldComponentUpdate(nextProps, nextState)react 性能优化非常重要的一环。组件接受新的 state 或者 props 时调用,我们可以设置在此对比前后两个 props 和 state 是否相同,如果相同则返回 false 阻止更新,因为相同的属性状态一定会生成相同的 dom 树,这样就不需要创造新的 dom 树和旧的 dom 树进行 diff 算法对比,节省大量性能,尤其是在 dom 结构复杂的时候
componentWillUpdata(nextProps, nextState) 组件初始化时不调用,只有在组件将要更新时才调用,此时可以修改 state。
render()
componentDidUpdate() 组件初始化时不调用,组件更新完成后调用,此时可以获取 dom 节点。

demo
import React from ‘react’;
import ReactDOM from ‘react-dom’;
class Content extends React.Component {
constructor(){
super();
this.state = {
test:0
}
}
componentWillMount() {
console.log(‘Component WILL MOUNT!’)
}
componentDidMount() {
console.log(‘Component DID MOUNT!’)
}
componentWillReceiveProps(newProps) {
console.log(newProps)
console.log(‘Component WILL RECEIVE PROPS!’)
}
shouldComponentUpdate(newProps, newState) {
console.log(newProps,newState)
return true;
}
componentWillUpdate(nextProps, nextState) {
console.log(nextProps,nextState)
console.log(‘Component WILL UPDATE!’);
}
componentDidUpdate(prevProps, prevState) {
console.log(prevProps,prevState)
console.log(‘Component DID UPDATE!’)
}
componentWillUnmount() {
console.log(‘Component WILL UNMOUNT!’)
}
_handleClick(){
this.setState({
test:this.state.test + 1
})
}
render() {
return (
<div>
<h3>{this.props.myNumber}</h3>
<label onClick={this._handleClick.bind(this)}> 点我 </label>
<span>{this.state.test}</span>
</div>
);
}
}

class Button extends React.Component {
constructor(props) {
super(props);
this.state = {data: 0};
this.setNewNumber = this.setNewNumber.bind(this);
}

setNewNumber() {
this.setState({data: this.state.data + 1})
}
render() {
return (
<div>
<button onClick = {this.setNewNumber}>INCREMENT</button>
<Content myNumber = {this.state.data}></Content>
</div>
);
}
}
ReactDOM.render(<Button />, document.getElementById(‘root’));
卸载
componentWillUnmount() 组件将要卸载时调用,一些事件监听和定时器需要在此时清除,主要是优化性能,例子见 Clock。

正文完
 0

react-生命周期

17次阅读

共计 746 个字符,预计需要花费 2 分钟才能阅读完成。

组件实例化
static defaultProps
组件初始化属性
constructor
组件内的构造函数,内部可以声明状态获取属性
componentWillMount
组件在渲染之前,会调用此生命周期函数
render
该方法会创建一个虚拟 DOM,用来表示组件的输出。对于一个组件来讲,render 方法是唯一一个必需的方法。render 方法需要满足下面几点:
只能通过 this.props 和 this.state 访问数据(不能修改)
可以返回 null,false 或者任何 React 组件
只能出现一个顶级组件,不能返回一组元素
不能改变组件的状态
不能修改 DOM 的输出
render 方法返回的结果并不是真正的 DOM 元素,而是一个虚拟的表现,类似于一个 DOM tree 的结构的对象。react 之所以效率高,就是这个原因。
componentDidMount
组件挂载完成,此时可以获取到真正的 dom 元素
运行时
componentWillReceiveProps
该生命周期函数只存在于子组件,当父组件传递过来的 props 发生变化时调用
shouldComponentUpdate(nextProps, nextState)
当 state 发生改变时,会先调用此方法,该方法默认会返回 true, 如果返回值为 true 则进入下一个声明周期,如果为 false 则不会在继续运行下面的生命周期函数。在该方法中,可以对 state 进行判断,来处理是否要重新渲染,从而做一定的优化
componentWillUpdate
shouldComponentUpdate 返回 true 时,触发该生命周期函数。
componentDidUpdate
整个组件重新 render 之后会调用该生命周期函数
componentWillUnmount
组件卸载时将会被调用
整个声明周期见下图

正文完
 0