React学习笔记(二)

2018-11-23  本文已影响0人  许小花花

生命周期

生命周期.jpg

一般将生命周期分成三个阶段:

创建阶段(Mounting)
更新阶段(Updating)
卸载阶段(Unmounting)

从 React v16 开始,还对生命周期加入了错误处理(Error Handling)。

React 16.3 新增的生命周期方法

getDerivedStateFromProps()
getSnapshotBeforeUpdate()

逐渐废弃的生命周期方法:

componentWillMount()
componentWillReceiveProps()
componentWillUpdate()

虽然废弃了这三个生命周期方法,但是为了向下兼容,将会做渐进式调整。(详情见#12028)
V16.3 并未删除这三个生命周期,同时还为它们新增以 UNSAFE_ 前缀为别名的三个函数 UNSAFE_componentWillMount()UNSAFE_componentWillReceiveProps()UNSAFE_componentWillUpdate()
在 16.4 版本给出警告将会弃用 componentWillMount()componentWillReceiveProps()componentWillUpdate()三个函数
然后在 17 版本将会删除 componentWillMount()componentWillReceiveProps()componentWillUpdate() 这三个函数,会保留使用 UNSAFE_componentWillMount()UNSAFE_componentWillReceiveProps()UNSAFE_componentWillUpdate()

创建阶段 Mounting

组件实例创建并插入 DOM 时,按顺序调用以下方法:
-constructor()
-static getDerivedStateFromProps()
-componentWillMount()/UNSAFE_componentWillMount()(不建议)
-render()
-componentDidMount()

有定义 getDerivedStateFromProps 时,会忽略 componentWillMount()/UNSAFE_componentWillMount()

constructor(props)

构造函数通常用于:

static getDerivedStateFromProps(nextProps, prevState)

当创建时、接收新的 props 时、setState 时、forceUpdate 时会执行这个方法。
注意:v16.3 setState 时、forceUpdate 时不会执行这个方法,v16.4 修复了这个问题。
这是一个 静态方法,参数 nextProps 是新接收的 props,prevState 是当前的 state。返回值(对象)将用于更新 state,如果不需要更新则需要返回 null。

class ExampleComponent extends React.Component {
  // Initialize state in constructor,
  // Or with a property initializer.
  state = {
    isScrollingDown: false,
    lastRow: null,
  };
 
  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;
  }
/* 父组件传入新的 props 时,用来和当前的 state 对比,判断是否需要更新 state。
以前一般使用 componentWillReceiveProps 做这个操作。 */

这个方法在建议尽量少用,只在必要的场景中使用,一般使用场景如下:

UNSAFE_componentWillMount()

这个方法已经不推荐使用。因为在未来异步渲染机制下,该方法可能会多次调用。它所行使的功能也可以由 componentDidMount()constructor()代替:
之前有些人会把异步请求放在这个生命周期,其实大部分情况下都推荐把异步数据请求放在 componentDidMount() 中。
在服务端渲染时,通常使用 componentWillMount() 获取必要的同步数据,但是可以使用 constructor() 代替它。
可以使用 setState,不会触发 re-render

render()

每个类组件中,render() 是唯一必须的方法。render() 正如其名,作为渲染用,可以返回下面几种类型:
-React 元素(React elements)
-数组(Arrays)
-片段(fragments)
-插槽(Portals)
-字符串或数字(String and numbers)
-布尔值或 null(Booleans or null)
注意:
Arrays 和 String 是 v16.0.0 新增。
fragments 是 v16.2.0 新增。
Portals 是 V16.0.0 新增。

里面不应该包含副作用,应该作为纯函数。

不能使用 setState。

componentDidMount()

组件完成装载(已经插入 DOM 树)时,触发该方法。这个阶段已经获取到真实的 DOM。
一般用于下面的场景:
-异步请求 ajax
-添加事件绑定(注意在 componentWillUnmount 中取消,以免造成内存泄漏)
可以使用 setState,触发re-render,影响性能。

更新阶段 Updating

-componentWillReceiveProps()/UNSAFE_componentWillReceiveProps()(不推荐)
-static getDerivedStateFromProps()
-shouldComponentUpdate()
-componentWillUpdate()/UNSAFE_componentWillUpdate()(不推荐)
-render()
-getSnapshotBeforeUpdate()
-componentDidUpdate()

getDerivedStateFromProps 或者 getSnapshotBeforeUpdate 时,componentWillReceiveProps()/UNSAFE_componentWillReceiveProps()componentWillUpdate()/UNSAFE_componentWillUpdate()不会执行 (详情查看源码)

UNSAFE_componentWillReceiveProps(nextProps)

这个方法在接收新的 props 时触发,即使 props 没有变化也会触发。一般用这个方法来判断 props 的前后变化来更新 state,如下面的例子:

class ExampleComponent extends React.Component {
  state = {
    isScrollingDown: false,
  };
 
  componentWillReceiveProps(nextProps) {
    if (this.props.currentRow !== nextProps.currentRow) {
      this.setState({
        isScrollingDown:
          nextProps.currentRow > this.props.currentRow,
      });
    }
  }
}

这个方法将被弃用,推荐使用 getDerivedStateFromProps 代替。
可以使用 setState

UNSAFE_componentWillUpdate(nextProps, nextState)

当接收到新的 props 或 state 时,在渲染前执行该方法。
在以后异步渲染时,可能会出现某些组件暂缓更新,导致 componentWillUpdatecomponentDidUpdate 之间的时间变长,这个过程中可能发生一些变化,比如用户行为导致 DOM 发生了新的变化,这时在 componentWillUpdate 获取的信息可能就不可靠了。
不能使用 setState

getSnapShotBeforeUpdate(prevProps, prevState)

这个方法在 render() 之后,componentDidUpdate() 之前调用。
两个参数 prevProps 表示更新前的 props,prevState 表示更新前的 state。
返回值称为一个快照(snapshot),如果不需要 snapshot,则必须显示的返回 null —— 因为返回值将作为 componentDidUpdate() 的第三个参数使用。所以这个函数必须要配合 componentDidUpdate() 一起使用。
这个函数的作用是在真实 DOM 更新(componentDidUpdate)前,获取一些需要的信息(类似快照功能),然后作为参数传给 componentDidUpdate。例如:在 getSnapShotBeforeUpdate 中获取滚动位置,然后作为参数传给 componentDidUpdate,就可以直接在渲染真实的 DOM 时就滚动到需要的位置。

class ScrollingList extends React.Component {
  constructor(props) {
    super(props);
    this.listRef = React.createRef();
  }
 
  getSnapshotBeforeUpdate(prevProps, prevState) {
    // Are we adding new items to the list?
    // Capture the scroll position so we can adjust scroll later.
    if (prevProps.list.length < this.props.list.length) {
      const list = this.listRef.current;
      return list.scrollHeight - list.scrollTop;
    }
    return null;
  }
 
  componentDidUpdate(prevProps, prevState, snapshot) {
    // If we have a snapshot value, we've just added new items.
    // Adjust scroll so these new items don't push the old ones out of view.
    // (snapshot here is the value returned from getSnapshotBeforeUpdate)
    if (snapshot !== null) {
      const list = this.listRef.current;
      list.scrollTop = list.scrollHeight - snapshot;
    }
  }
 
  render() {
    return (
      <div ref={this.listRef}>{/* ...contents... */}</div>
    );
  }
componentDidUpdate(prevProps, prevState, snapshot)

这个方法是在更新完成之后调用,第三个参数 snapshot 就是 getSnapshotBeforeUpdate 的返回值。
正如前面所说,有 getSnapshotBeforeUpdate 时,必须要有 componentDidUpdate。所以这个方法的一个应用场景就是上面看到的例子,配合 getSnapshotBeforeUpdate 使用。
可以使用 setState,会触发 re-render,所以要注意判断,避免导致死循环。

卸载阶段 Unmounting
componentWillUnmount()

在组件卸载或者销毁前调用。这个方法主要用来做一些清理工作,例如:
-取消定时器
-取消事件绑定
-取消网络请求
不能使用 setState

错误处理 Error Handling
componentDidCatch(err, info)

任何子组件在渲染期间,生命周期方法中或者构造函数 constructor 发生错误时调用。

错误边界不会捕获下面的错误:
-事件处理 (Event handlers) (因为事件处理不发生在 React 渲染时,报错不影响渲染)
-异步代码 (Asynchronous code) (e.g. setTimeout or requestAnimationFrame callbacks)
-服务端渲染 (Server side rendering)
-错误边界本身(而不是子组件)抛出的错误

上一篇 下一篇

猜你喜欢

热点阅读