共计 5193 个字符,预计需要花费 13 分钟才能阅读完成。
写在前边
这是一片记录 react 生命周期的文章。博主最近在重读 react 官网,想对 react 有更深度的理解。如果你同样对 react 这个优秀的框架抱有兴趣,欢迎联系我一同探讨!!文中有描述含混不清和错误的地方,望不吝赐教,直接指出
文末附有验证生命周期 API 先后顺序的 demo
QQ: 272473132
github: https://github.com/CregskiN
1. 基础概念
Mount、Update、Unmount
- Mounting:组件初次渲染
- Updating:因某些原因,组件需要更新
- Unmounting:组件卸载。即他的父组件的 render() 函数中不再 return 他
渲染阶段、预提交阶段、提交阶段
- Render phase:react 负责将 JSX 渲染为 DOM 对象(仅渲染,不更新页面)
一旦 state 或 props 改变,Render phase 阶段将被强制重新执行(具体次数无法预计)。所以尽量不要 Render phase 更新 state 或 props。
- Pre-commit phase:react 渲染完 DOM,预提交阶段,在这里,可以读取 DOM 信息
- Commit phase:react 渲染并完成 DOM 更新
官方建议:仅在这个阶段执行副作用、state 更新
2. Mounting 阶段
Render phase
-
constructor()
-
初始化 state、props
- 初始化 state 不要用 props.color,可以直接用 this.props.color
- 为事件处理 绑定 this
-
-
static getDerivedStateFromProps(props, state)
-
用处罕见:派生状态组件,即 state 在任何时候都取决于 prop
不建议使用派生组件,会导致受控于非受控含混不清,state 混乱
- return newState 返回对象以更新 state,若返回 null,不更新任何内容(无法使用 this.state 修改 state)
-
-
render()
当
render
被调用时,它会检查this.props
和this.state
的变化并返回以下类型之一:-
React 元素 。通常通过 JSX 创建。例如,
会被 React 渲染为 DOM 节点,
会被 React 渲染为自定义组件,无论是还是
均为 React 元素。 - 数组或 fragments。使得 render 方法可以返回多个元素。欲了解更多详细信息,请参阅 fragments 文档。
- Portals。可以渲染子节点到不同的 DOM 子树中。欲了解更多详细信息,请参阅有关 portals 的文档
- 字符串或数值类型 。它们在 DOM 中会被渲染为文本节点
-
布尔类型或
null
。什么都不渲染。(主要用于支持返回test &&
的模式,其中 test 为布尔类型。)
-
React 元素 。通常通过 JSX 创建。例如,
Pre-commit phase
Commit phase
-
componentDidMount()
- this.setState() 触发额外渲染,但会在浏览器更新屏幕之前
- 副作用 (side effect)
3. Updating 阶段
当组件的 props 或 state 发生变化时会触发更新。组件更新的生命周期调用顺序如下:
Render phase
static getDerivedStateFromProps()
-
shouldComponentUpdate(nextProps, nextState)
- return false ? 不调用 Updating 阶段的 render() : 调用 Updating 阶段的 render();
render()
Pre-commit phase
-
getSnapshotBeforeUpdate(prevProps, prevState)
-
在以下场景调用:
- 想要在组件改变之前获取 DOM 信息,如滚动位置
-
博主疑问:
为什么只有 Updating 阶段有 getSnapshotBeforeUpdate 而 Mounting 阶段没有?
是因为在 Mounting 阶段读取 DOM 的需求吗?
还是说官方建议 Mounting 阶段读取 DOM 的逻辑放到 componentDidMount 执行?
Commit phase
-
componentDidUpdate(prevProps, prevState, snapshot)
-
this.setState() 额外渲染,影响性能!!而且一定用 if 包裹,否则会死循环
componentDidUpdate(prevProps) { // 典型用法(不要忘记比较 props):if (this.props.userID !== prevProps.userID) {this.fetchData(this.props.userID); } }
-
4. Unmounting 阶段
当组件从 DOM 中移除时会调用如下方法:
Render phase
Pre-commit phase
Commit phase
componentWillUnmount()
5. other lifecycle
错误处理
当渲染过程,生命周期,或子组件的构造函数中抛出错误时,会调用如下方法:
Render phase
-
static getDerivedStateFromError(error)
- return newState;
Commit phase
-
componentDidCatch(error, info)
- error:Error 实例
- info:React.ErrorInfo 包含错误栈信息
父组件 Lifecycle.tsx
import React, {Component} from 'react';
import CanUnmount from './CanUnmount';
interface LifecycleProps { };
interface LifecycleState {counter: number;};
function tableLog(logName: string, obj: any) {if (obj !== null && obj !== undefined) {if (typeof obj === 'object' && obj.length) {console.log(logName);
console.table(obj);
}
}
}
class lifecycle extends Component<LifecycleProps, LifecycleState> {handleAdd() {this.setState((state, props) => ({counter: state.counter + 1}))
}
handleLess() {this.setState((state, props) => ({counter: state.counter - 1}))
}
handleReset() {this.setState((state, props) => ({counter: 0}))
}
// Mounting
constructor(props: LifecycleState) {super(props);
this.state = {counter: 0,};
console.group({lifecycle: 'constructor'});
console.table(this.state)
console.groupEnd();
this.handleAdd = this.handleAdd.bind(this);
this.handleLess = this.handleLess.bind(this);
this.handleReset = this.handleReset.bind(this);
}
static getDerivedStateFromProps(props: LifecycleProps, state: LifecycleState) {
// render-phase 获得来自副组件的 props 派生
console.group({lifecycle: 'getDerivedStateFromProps'});
tableLog('props', props);
tableLog('state', state);
console.groupEnd();
return null;
}
render() {console.group({ lifecycle: 'render'});
console.groupEnd();
const {counter} = this.state;
const {handleAdd, handleLess, handleReset} = this;
if (counter < 3) {
return (
<div>
{counter}
<button onClick={handleAdd}>+1</button>
<button onClick={handleLess}>-1</button>
</div>
)
} else {
return (
<div>
{counter}
<button onClick={handleReset}>reset</button>
<CanUnmount />
</div>
)
}
}
componentDidMount() {console.group({ lifecycle: 'componentDidMount'});
console.log('Mounting finished')
console.groupEnd();
console.log('\n');
}
// Updating
// static getDerivedStateFromProps
shouldComponentUpdate(nextProps: LifecycleProps, nextState: LifecycleState) {console.group({ lifecycle: 'shouldComponentUpdate'});
tableLog('nextProps', nextProps);
tableLog('nextState', nextState);
console.groupEnd();
if (this.state.counter !== nextState.counter) {return true;}
return false;
}
// render
getSnapshotBeforeUpdate(prevProps: LifecycleProps, prevState: LifecycleState) {console.group({ lifecycle: 'getSnapshotBeforeUpdate'});
tableLog('prevProps', prevProps);
tableLog('prevState', prevState);
console.log('getSnapshotBeforeUpdate finished');
console.groupEnd();
return {snapshot: 'ss'};
}
componentDidUpdate(prevProps: LifecycleProps, prevState: LifecycleState, snapshot: any) {console.group({ lifecycle: 'componentDidUpdate'});
tableLog('prevProps', prevProps);
tableLog('prevState', prevState);
tableLog('snapshot', snapshot);
console.log('Update finished');
console.groupEnd();
console.log('\n');
}
// Upmounting
componentWillUnmount() {console.group({ lifecycle: 'componentWillUnmount'});
console.log('Unmounting finished');
console.groupEnd();
console.log('\n');
}
}
export default lifecycle;
子组件 CanUnmoun.tsx
import React, {Component} from 'react';
interface CanUnmounProps { };
class CanUnmoun extends Component {componentWillUnmount() {console.group({ lifecycle: 'CanUnmount componentWillUnmount'});
console.log('CanUnmount Unmounting finished');
console.groupEnd();
console.log('\n');
}
render() {
return (
<div>
Component CanUnmoun
</div>
)
}
}
export default CanUnmoun;
Reference
React.Component
快速了解 React 的新功能 Suspense 和 Hooks