你是否在理解React生命周期方法和如何优雅的使用上遇到了困难?
别担心,你并不孤单。在我第一次编写React应用时,我遇到了很多情况导致我不得不退一步重新学习React的生命周期方法来让自己能够继续开发。
在开始之前,我想让你明白的是最新版本的React已经决定在React 17版本移除一些不安全的旧的生命周期方法。在这个博文中我们在不会学习这些即将被移除的不安全的生命周期方法。
什么是React生命周期方法
你可以将React生命周期方法理解为是React组件从出生到死亡所经历的一系列的事件
React中的每个组件都会经历生命周期的事件,我倾向于理解为它们会经历出生、成长和死亡这样的一个周期。
- 挂载时(Mounting)- 组件的出生
- 更新时(Updating)- 组件的成长
- 卸载时(Unmounting)- 组件的死亡
常见的React生命周期方法**
render()
render()
方法是最常见的生命周期方法,你会在所有的类组件中看见这个方法。这是因为render()
方法是React中类组件唯一一个必须有的方法。
下面是React中redner()
的一个简单示例
class Hello extends Component {
render() {
return <div>Hello {this.props.name}</div>
}
}
正如你再上面看见的一样,reder()
方法返回用于展示UI的JSX。一个render()
方法可以返回布尔值或者null来表示什么都渲染。
_render()_
方法是一个没有副作用的纯函数
React要求render()
函数是一个纯函数。纯函数就表示其没有任何副作用,在输入相同的情况下一定返回相同的输出。也就意味着你不能再render()
中使用setState()
。
你不能再redner()
修改组件状态(state)
_
如果你需要改变状态,那应该在其他生命周期方法中进行。总之要保证render()
纯粹。
此外,保证render()
简单、干净且没有状态更新能提升你的应用的可维护性。
componentDidMount()
现在你的组件已经挂载并准备好了,接下来的生命周期方法是componentDidMount()
。
componentDidMount
在组件挂载并准备好时被调用。如果你需要加载远端数据时那么这里是调用API的好地方。
和render()
不同的是componentDidMount()
允许使用setState()
,在这里调用setState()
将会更新状态并造成新的渲染,不过会发生在浏览器更新UI之前。这是为了确保用户不会在两次渲染时看见UI刷新。
你可以在componentDidMount()
中修改组件状态,但是要小心使用
_
注意:建议你小心使用这种模式,因为可能会导致性能问题。最佳的操作是在constructor()
中进行状态初始化。React允许在这个生命周期方法使用setState()
的原因是因为一些特殊的需求比如是tooltips、modals等需要在渲染前测量DOM节点位置。
componentDidUpdate()
这个方法会在更新发生时尽快调用。最常用的场景是根据props或者state的变化更新DOM。
你可以在这个生命周期内调用setState()
,但是需要注意的是要根据props和state的变化判断是否需要调用。厕错误的使用会导致无限循环渲染。
你可以通过componentDidUpdate()
修改state,但是使用时需要当心
_
下文是这个生命周期方法的典型用例
componentDidUpdate(prevProps) {
// 注意此处对于props的比较
if (this.props.userName !== prevProps.userName) {
this.fetchData(this.props.userName);
}
}
注意在上文的示例中我们对当前的props和之前的props进行了比较,这里是为了知道props是否发生了变化,如果没有产生变化则不会进行API的调用,state的处理也是一样。
componentWillUnmont()
从名字上可以看出这个生命周期方法仅会在组件卸载且被销毁之前调用,如果你需要进行一些清除操作(比如取消监听、清除定时器等)则可以在这个方法中调用。
你不能在componentWillUnmount
周期内修改组件状态(state)
_
这个组件永远不会重新渲染,因为我们不能在这个方法内调用setState()
。
componentWillUnmount() {
window.removeEventListener('resize', this.resizeListener);
}
在这里进行的清除操作一般是清除定时器、取消api调用或者清除缓存等。
不常用的React生命周期方法
我们现在对常用的React生命周期方法有了了解,除此之外,React还提供了其他的不怎么常用甚至没有用到过的生命周期方法。
shouldComponentUpdate()
这个生命周期方法在你不想因为props或者state的改变造成组件刷新时很好使用。任何情况下调用setState()
方法都会默认重新渲染组件,而shouldComponenUpdate()
可以用来告诉React此组件不受某些props或者state的影响。
在使用这个方法时需要小心,它的存在仅仅是为了进行性能优化,你不能在shouldComponentUpdate()
中更新组件的state。
注意:最重要的是,不要总是依赖这个方法来阻止组件渲染,这样可能会导致一些bug
shouldComponentUpdate(nextProps, nextState) {
return this.props.title !== nextProps.title ||
this.state.input !== nextState.input }
在上面展示的示例中,这个方法应该总是返回一个布尔值来决定是否渲染这个组件。
static getDerivedStateFromProps()
这是React团队最新推出的一个生命周期方法。
这将是用来替换componentWillReceiveProps()
的更为安全的方案。
它将在render()
之前被调用。
这是一个静态方法,所以不能访问this
。getDerivedFromProps()
返回一个取决于props变化的对象用以更新state,也可以返回null来表示不需要更新state。
这个方法的存在是为了解决组件在某些特殊的情况下state的值取决于props的问题。
static getDerivedStateFromProps(props, state) {
if (props.currentRow !== state.lastRow) {
return {
isScrollingDown: props.currentRow > state.lastRow,
lastRow: props.currentRow,
};
}
// Return null to indicate no change to state.
return null;
}
需要注意的是这个方法会在每次组件渲染前调用。
一个使用此方法比较便利的场景是<Transition>
组件通过比较前后children来决定哪个进行出场/入场动画。
getSnapshotBeforeUpdate()
getSnapshotBeforeUpdate()
是另一个React新推出的生命周期方法。这个是用来替换componentWillUpdate()
的更为安全的方案。
getSnapshotBeforeUpdate(prevProps, prevState) {
// ...
}
这个会在更新之前被调用,从getSnapshotBeforeUpdate()
返回的值会传递给componentDidUpdate()
。
请记住,这个方法应该尽量少点使用甚至是不适用。
在异步渲染期间改变窗口尺寸是使用getSnapshotBeforeUpdate()
的好地方。
React组件生命周期图表
下面的图表展示了不同的react组件生命周期函数以及它们合适调用。
回顾
- React组件生命周期包括三个阶段:挂载时(Mounting)、更新时(Updating)、卸载时(Unmounting)
render()
是最常用的生命周期方法- 纯函数
- 不能在
render()
时修改state
componentDidMount()
会在组件挂载后调用- 你可以在这儿修改state,不过要小心
componentDidUpdate()
会组件更新后调用- 你可以在这儿修改state,不过要小心
componentWillUnmount()
仅会在组件卸载且被销毁之前调用- 这儿可以进行清除操作
- 不能在这儿修改state
shouldComponentUpdate()
很少使用- 这个生命周期方法在你不想因为props或者state的改变造成组件刷新时很好使用
- 它的存在仅仅是为了进行性能优化
- 有两个新的生命周期方法是
getDerivedStateFromProps()
和getSnapshotBeforeUpdate()
- 它们都是偶尔使用
- 它们的使用场景比较少,并且还在讨论中,之后会有更多的示例
原帖地址:https://programmingwithmosh.com/javascript/react-lifecycle-methods/