React16 后的各功能点是多个版本陆陆续续迭代增加的,本篇文章的讲解是建立在
16.6.0版本上
本篇文章主旨在介绍 React16 之后版本中新增或修改的地方,所以对于 React16 之前版本的功能,本篇文章当作您已充分了解了,不再赘述
从 React v16.0 ~ React v16.6 的更新概览(只涉及部分常用api):
render支持返回数组和字符串createContextcreateRef更新 getDerivedStateFromProps
memolazystatic contextTypestatic getDerivedStateFromErrorHooks
接下来将针对影响较大,使用频率较高的更新点逐一讲解。
我们知道,对 React 组件的性能优化,shouldComponentUpdate函数是很重要的一啪,所以 React 才会在 React.Component的基础上增加了React.PureComponent,但是对于非class类的纯函数写法,却没法增加这样的便捷处理。
对于这个问题,React16.6 增加了React.memo这个高阶组件
一般使用方式:1
2
3const C = React.memo(props => {
// xxx
})
React.memo的实现类似React.PureComponent,所以它内部是对对象进行浅比较。React.memo允许你自定义比较方法,如下:1
2
3
4
5
6
7// 函数的返回值为 true 时则更新组件,反之则不更新
const equalMethod = (prevProps, nextProps): boolean => {
// 定义你的比较逻辑
}
const C = React.memo(props => {
// xxx
}, equalMethod)
React生命周期分为三个阶段:挂载、更新、卸载,React16后又多了一个异常,我们一一看下。
constructorstatic getDerivedStateFromPropsrendercomponentDidMountrender和componentDidMount较 React16 之前无变化。对于挂载过程,我们着重看下constructor、componentWillMount和static getDerivedStateFromProps。
初始化 state
注意:应避免使用props给state赋值,这样的话, state的初始化可以提到constructor外面处理
1 | constructor(props) { |
给方法绑定this
1 | constructor(props) { |
但是,以上两件事放到constructor外面处理会更简单些,如下:
1 | class C extends React.Component { |
所以,React16 以后用到constructor的场景会变少。
可以看到,componentWillMount在 React16 中被“删掉”了(这样说其实是有问题的,因为 React 并未真正删除该生命周期函数,只是告诫开发者,该函数在未来版本中会被废弃掉),那么问题就出现了,原先在这个生命周期中的做的事情,现在该放到哪里去做呢?
首先问自己一个问题,原先的时候都在这个生命周期里做什么?答案是大部分时候会在这里做 AJAX 请求,然后执行setState重新渲染。
然而在componentWillMount里做 AJAX 请求实在不是一个明智之举,因为对于同构项目中,componentWillMount是会被调用的。
还有人会在这里面初始化state,关于state的初始化,请参看楼上小节。
综上所述,componentWillMount其实本来没有什么主要作用,如果你的代码规范,去掉的话,不会对现在的项目产生什么影响。
上面我们讲到,应避免使用props给state赋值,但是在 React16 前我们都是这么做的,现在如果不让这么操作了,那该在哪里处理这块逻辑呢? React16 给出的答案就是 static getDerivedStateFromProps。
挂载组件时,该静态方法会在render前执行;更新组件时,该静态方法会在shouldComponentUpdate前执行。
1 | class C extends React.Component { |
getDerivedStateFromProps的返回值将作为setState的参数,如果返回null,则不更新state,不能返回object 或 null 以外的值,否则会警告。
getDerivedStateFromProps是一个静态方法,是拿不到实例this的,所以开发者应该将该函数设计成纯函数。
这样,有没有发现componentWillReceiveProps也就没有用武之地了?是的,React16 把它也“删掉”了(这样说其实是有问题的,因为 react 并未真正删除该生命周期函数,只是告诫开发者,该函数在未来版本中会被废弃掉,建议使用更好的getSnapshotBeforeUpdate 或 getDerivedStateFromProps)
static getDerivedStateFromPropsshouldComponentUpdaterendergetSnapshotBeforeUpdatecomponentDidUpdatestatic getDerivedStateFromProps前面已经介绍过了,而其他的几个生命周期函数与 React16 之前基本无异,所以这里主要介绍下getSnapshotBeforeUpdate。
在 React 更新 DOM 之前调用,此时state已更新;
返回值作为componentDidUpdate的第3个参数;
一般用于获取render之前的 DOM 数据
语法:1
2
3
4
5
6
7
8class C extends React.Component {
getSnapshotBeforeUpdate (prevProps, prevState): Snapshot {
}
componentDidUpdate(prevProps, prevState, snapshot) {
// snapshot 是从 getSnapshotBeforeUpdate 的返回值,默认是 null
}
}
getSnapshotBeforeUpdate 的使用场景一般是获取组建更新之前的滚动条位置。
componentWillUnmount
较之前无变化。
componentDidCatch 这个函数是 React16 新增的,用于捕获组件树的异常,如果render()函数抛出错误,则会触发该函数。可以按照 try catch 来理解和使用,在可能出现错误的地方,使用封装好的包含 componentDidCatch 生命周期的组建包裹可能出错的组件。
1 | class PotentialError extends React.Component { |
如:1
2
3
4
5const Demo = () => (
<PotentialError>
<div>{{a: 1}}</div>
</PotentialError>
)
这样,Demo 组件即使直接使用对象作为子组件也不会报错了,因为被 PotentialError 接收了。
看看穿上新生命周期这身新衣服后的样子吧
1 | import React from 'react' |
【未完待续】