Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

React生命周期梳理 #23

Open
pomelovico opened this issue Apr 17, 2019 · 0 comments
Open

React生命周期梳理 #23

pomelovico opened this issue Apr 17, 2019 · 0 comments
Labels
JavaScript about javascript

Comments

@pomelovico
Copy link
Owner

从官网最新提供的生命周期示意图来看,React的class组件的阶段仍然分为三个阶段:组件挂载,组件更新以及组件卸载阶段,从16.4版本后,React废除了一些生命周期方法的同时也增加了几个新的方法来满足和改进组件的需求和开发体验。
image.png

挂载阶段

constructor

constructor(props)是class组件创建时调用的方法,一般做三件事

  • 在方法最前面使用super关键字获取来自父组件的props(通常是继承React.Component的子类需要实现的,否则组件的this.props为undefined)
  • 为组件上的方法绑定上下文(该实例)
  • 为组件初始化内部状态属性state

如果仅仅是为了初始化state,且state不依赖props,可以不使用constructor方法,直接使用在新的类语法定义:

class App extends React.Component{
  //初始化状态
  state = {
    count:0
  }
  /*....*/
}

constructor方法是唯一可以直接给组件state赋值的方法,且不必使用setState()方法。

父组件的props与初始化state联系:
很多时候我们在初始化组件的state时,会根据组件收到的props来初始化自身的state,比如:

constructor(props){
  super(props)
  this.state = {
    color:props.color
  }
}

这种写法大概很常见,官方注明是一种误用(mistake),因为父组件的props更新并不会反映到state上来,除非我们又在componentWillReceiveProps(已废弃方法)或者getDerivedStateFromProps(新增方法)中重新为state赋值,但这样的方式违背了React的最佳实践原则,使得代码冗余。
但是使用这种方式也有它的意图,比如组件希望以来自于父组件的props作为自己的state,然后自身维护这个state。个人遇到的一些场景比如表单更新的场景,表单组件需要接收一些来自于父组件传递的用户已保存的数据作为自身的初始化数据,而后表单组件自身又需要对这些数据进行维护,所以不得不用state保存这些props。官方提示可以通过使用key的方式来强制组件重新更新state(组件的key属性变化时,React会重新渲染整个组件

getDerivedStateFromProps

static getDerivedStateFromProps(props,state)方法在每次render方法触发前调用,不论是组件创建和更新阶段,不论是props变化还是state变化,都会调用,该方法是为了替代原来的componentWillReceiveProps方法(原来的方法只有props变化才会调用,与state变化无关)。该方法为开发人员提供了一个在渲染组件前根据props更新state的机会。但不同的是,该方法需要返回一个新的对象作为新的state或者返回null表示state不需要更新,而在原来的生命周期方法里,是通过setState来更新state。

**值得注意的是:**通常我们使用此周期方法是为了根据props的变化来更新组件自身的state,但是官方博客里提到不是特别推荐使用该方法,因通过props改变state使得编写的组件更难以读懂,因此官博里提到如果有以下几种场景,可以考虑替代的方法:

  • 如果为了响应props的变化而执行一些具有副作用的操作(如获取数据或者执行动画),建议将此操作放入到componentDidUpdate生命周期里
  • 如果想根据props;的变化重新计算一些数据,则建议使用辅助的缓存工具
  • 如果需要根据props的变化重置一些state,则建议直接使用受控的函数组件,或者用key来实现重新组件的渲染

getDerivedStateFromProps方式是一个静态方法,因此其不会访问组件实例,官博提示可以将一些抽取出一些独立于类定义的纯函数以实现复用。

render

render()方法是类组件里唯一要求必须实现的方法,用于输出该组件的内容结构。render方法执行时,可以访问到prop和state,其返回以下几种类型数据结构:

  • React Element,通过JSX语法提供的快速原生的HTML标签或者用户自定义组件
  • **数组或者片段(Arrays and Fragments),**现在的版本可以返回多个元素数组或者特殊的React.Fragment
  • Portals,Portals可以将子元素渲染到不同的DOM子树下,通常用于弹框等组件
  • 字符串或者数字,作为文本节点渲染
  • **Boolean 或者 null,**什么也渲染,通常用于支持短路运算符如return test && <Child/>

render方法应该是一个纯函数,意味着不能修改state或者props,在此方法中一般不与浏览器交互

shouldComponentUpdate返回false的时候,render函数不会执行

componentDidMount

componentDidMount()方法在组件挂载到真实DOM节点后执行,其在render方法之后执行。此方法多用于执行一些数据获取,事件监听等操作。

可以在componentDidMount方法里立即执行setState()方法,这将会组件再渲染一次(更新阶段),但是这只会发生在浏览器更新屏幕之前,因此保证了即使在这种情况下render函数被调用了两次用户也不会察觉到中间的状态变化。官方提示要谨慎使用这种模式,因为其可能会造成一定的性能问题。这种使用方式通常是在需要依赖真实DOM节点的信息(如位置或者大小)来渲染一些其他的组件时用到。

而数据获取或者事件监听中的setState()方法是异步的,不会造成上述问题。

更新阶段

getDerivedStateFromProps

(略)

shouldComponentUpdate

shouldComponentUpdate(nextProps,nextState)方法用于告知组件本身基于当前的props和state是否需要重新渲染组件,如果在class组件中未声明此方法,则默认返回true(默认更新)。

此方法在收到新的props或者state时都会调用,通过返回true或者false告知组件更新与否。在首次render以及使用了forceUpdate时此方法不会被调用。

此方法存在的意义在于性能优化而不是阻止组件本身的渲染,React提供了React.PureComponent来实现props与state的浅比较(shallow comparison),避免了手动编写此方法。

官博提示当前版本的shouldComponentUpdate方法返回false时,将不会触发componentWillUpdate、render以及componentDidUpdate方法。在未来版本中,即使返回false也会重新渲染。

render

(略)

getSnapshotBeforeUpdate

getSnapshotBeforeUpdate(prevProps,prevState)方法在最近一次组件渲染结果更新到真实DOM之前触发,也就是虚拟DOM向真实DOM更新之前触发。方法返回的值会作为第三个参数传递给componentDidUpdate方法

此方法的目的在于获取组件更新前的一些信息,比如组件的滚动位置之类的,在组件更新后可以根据这些信息恢复一些UI视觉上的状态。官博中给出的滚动列表例子中,保存了当前滚动列表的位置,保证在列表添加了新的元素后,仍然能够使其恢复到原来的滚动位置。因此snapshot(快照)跟这个方法名很贴切,记录了组件更新前的样子。

个人觉得此方法更多的用在动画,视觉方面而不是数据的处理上。总的来说是方便了记录和传递中间状态,而不像以前要在componentWillUpdate方法(已废除)里做额外的工作。

componentDidUpdate

componentDidUpdate(prevProps,prevState,snapshot)方法在组件更新结束后触发,此周期中,可以根据前后的props和state的变化做相应的操作,如获取数据,修改DOM样式等。此方法与componentDidMount的工作类似,但其可以多次被调用,而后者在整个组件生命周期中只被调用一次。

**注意: **不能在此方法中直接调用setState()方法,否则或造成无限循环更新。

卸载阶段

componentWillUnmount

此方法用于组件卸载前,清理一些注册是监听事件,或者取消订阅的网络请求等。一旦一个组件实例被卸载,其不会被再次挂载,而只可能是被重新创建。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
JavaScript about javascript
Projects
None yet
Development

No branches or pull requests

1 participant