组件生命周期函数
当组件实例被创建并插入 DOM
中时,其生命周期调用顺序如下:
constructor(props)
在组件挂载之前会先调用该方法,在实现构造函数时必须先调用super(props)
方法,否则会出现BUG
通常,构造函数仅用于两种情况:1. 初始化 state
2. 为事件处理函数绑定实例
在该方法中不要使用 setState()
方法,在其他方法中使用setState()
改变 state
为什么 props 复制给 state 会产生 bug
constructor(props) {
super(props);
// 不要在这里调用 this.setState()
this.state = {
counter: 0,
name:props.name // 严禁这样赋值,props.name值更新时 state.name并不会更新
};
this.handleClick = this.handleClick.bind(this);
}
static getDerivedStateFromProps() (此方法不常用)
此方法适用于 state 值在任何时候都取决于props 的情况。
render()
当该方法被调用时,它会监测 props 和 state 的变化,并且返回以下类型之一:
React 元素
:通过JSX创建,渲染成对应的DOM节点或自定义组件- 数组或fragments: 使render方法可以返回多个元素 frgments
Portals
:可以渲染子节点到不同的DOM子树汇中portals- 字符串或数值类型: 在DOM中会被渲染为文本节点、
Boolean 或 null
:什么都不渲染
render方法最好为纯函数,即在不修改组件 state
情况下,每次调用时都返回相同的结果,并且不会直接与浏览器交互
class Example extemds React.Component{
shouldComponentUpdate(nextProps, nextState){
return false
}
render(){ // 不会执行
<div>owen</div>
}
}
componentDIdMount()
组件的 props
或 state
发生变化会触发更新。组件更新的生命周期调用顺序如下:
static getDerivedStateFromProps() (此方法不常用)(已解释)
shouldComponentUpdate(nextProps, nextState) (此方法不常用)
根据该方法的返回值判断组件输出是否受当前 state 或 props 更改的影响。默认为 state 每次更新重新渲染
此方法进仅做为性能优化的方式存在,不要企图依靠此方法来“阻止”渲染,因为这可能会产生 bug。你应该考虑使用内置的 PureComponent 组件,而不是手动编写 shouldComponentUpdate()。PureComponent 会对 props 和 state 进行浅层比较,并减少了跳过必要更新的可能性。
render()(已解释)
getSnapshotBeforeUpdate() (此方法不常用)
class ScrollingList extends React.Component {
constructor(props) {
super(props);
this.listRef = React.createRef();
}
getSnapshotBeforeUpdate(prevProps, prevState) {
// 我们是否在 list 中添加新的 items ?
// 捕获滚动位置以便我们稍后调整滚动位置。
if (prevProps.list.length < this.props.list.length) {
const list = this.listRef.current;
return list.scrollHeight - list.scrollTop;
}
return null;
}
componentDidUpdate(prevProps, prevState, snapshot) {
// 如果我们 snapshot 有值,说明我们刚刚添加了新的 items,
// 调整滚动位置使得这些新 items 不会将旧的 items 推出视图。
//(这里的 snapshot 是 getSnapshotBeforeUpdate 的返回值)
if (snapshot !== null) {
const list = this.listRef.current;
list.scrollTop = list.scrollHeight - snapshot;
}
}
render() {
return (
<div ref={this.listRef}>{/* ...contents... */}</div>
);
}
}
上述示例中,重点是从 getSnapshotBeforeUpdate 读取 scrollHeight 属性,因为 “render” 阶段生命周期(如 render)和 “commit” 阶段生命周期(如 getSnapshotBeforeUpdate 和 componentDidUpdate)之间可能存在延迟。
componentDidUpdate(prevProps, prevState, snapshot)
componentDidUpdate(prevProps) {
// 典型用法(不要忘记比较 props):
if (this.props.userID !== prevProps.userID) {
this.fetchData(this.props.userID);
}
}
注意:如果 shouldComponentUpdate() 返回值为 false,则不会调用 componentDidUpdate()。
当组件从 DOM
中移除时会调用如下方法:
componentWillUnmount()
当渲染过程,生命周期,或子组件的构造函数中抛出错误时,会调用如下方法:
Error boundaries:捕获渲染期间及整个树的函数发送的错误,渲染降级 UI,但自身的错误无法捕获 React 16中的错误处理
static getDerivedStateFromError(error) (此方法不常用)
componentDidCatch(error, info) (此方法不常用)
如果发生错误,可以通过调用 setState
使用 componentDidCatch()
渲染降级 UI,但在未来的版本中将不推荐这样做。 可以使用静态 getDerivedStateFromError()
来处理降级渲染。
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
// 更新 state 使下一次渲染可以显降级 UI
return { hasError: true };
}
componentDidCatch(error, info) {
// "组件堆栈" 例子:
// in ComponentThatThrows (created by App)
// in ErrorBoundary (created by App)
// in div (created by App)
// in App
logComponentStackToMyService(info.componentStack);
}
render() {
if (this.state.hasError) {
// 你可以渲染任何自定义的降级 UI
return <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}
Example
class Square extends React.Component {
constructor(props) {
super(props);
this.state = {
value:null
};
}
static getDerivedStateFromProps(props, state) {
// 实例化组件之后以及在重新呈现组件之前调用新的静态生命周期。它可以返回要更新的对象state,或null指示新对象props不需要任何state更新。
}
componentDidMount() { // 组件被渲染到 DOM 中后运行
console.log('DidMount: 1')
}
shouldComponentUpdate(){
// 更新前
}
getSnapshotBeforeUpdate(){
//
}
componentDidUpdate() {
// 更新后
}
static getDerivedStateFromError() {
// 出错时
}
componentDidCatch(){
// capture error
}
compoentwillUnmount(){ // 组件被删除的时候
console.log('UnMount: end')
}
render() {
return (
<button className="square" onClick = {()=>{this.setState({value:'X'})}
}>
{this.state.value}
</button>
);
}
}