理解React-组件生命周期

1,793 阅读8分钟

作者-Bartosz Szczeciński原文

React提供给开发者很多方法或‘钩子’,调用于一个组件的生命周期,使我们更新UI和app state。了解何时使用哪些对正确理解如何使用React至关重要。

constructor

constuctors是OOP的基础——一个无论何时一个新object被创建时被调用的特殊函数。调用一个特殊函数super非常重要,在我们的class extent任何其它class(也有定义了constructor)时。调用此特殊函数将call 我们parent class的constructor且允许初始化其本身。此即为何我们只有在初始化地call了super时才有权限访问this.props的原因。

如上所述,constructor对于构建我们组件——创建任何领域(以this.开始的变量)或者初始化以接收到的props为基础的state——非常完美。

这也是你通过在直接覆盖this.state之处期待change/set state仅有的字段。在别的其它实例上记得用this.setState.

DO

  • set initial state
  • 若未用class属性语法-准备所有class 字段和bind 将作为callback传递的函数。

DON'T

  • cause any side effects (AJAX calls etc.)

componentWillMount

这是一个特殊情况 - componentWillMount与构造函数没有多大区别 - 它只在初始安装生命周期中被调用一次。 从历史上看,使用componentWillMountover构造函数有一些原因,请参阅此react-redux问题,但请记住,此处描述的练习已被弃用。

很多人会试图使用这个函数来发送请求来获取数据,并希望在初始渲染准备好之前数据可用。 情况并非如此 - 当请求在渲染之前被初始化时,在渲染被调用之前它将不能完成。

此外,随着对React Fiber的更改(post React 16 beta版本),此函数最终可能会在初始渲染被调用之前调用多次,因此可能会导致触发多重副作用。 由于这个事实,不推荐使用这个函数来产生任何副作用。

值得注意的是,在使用服务器端渲染时调用此函数,而在这种情况下,它不会在服务器上调用对应的componentDidMount,而是在客户端上调用。 所以如果在服务器部分需要一些副作用,这个函数应该被用作例外。

此函数中使用的setState是“空闲”的,不会触发重新渲染。

DO

  • update state 通过 this.setState
  • perform last minute optimization(执行最后一分钟优化)
  • 仅在SSR造成side-effects (AJAX calls etc.)

DON'T

  • 不在CS造成side-effects (AJAX calls etc.)

componentWillReceiveProps(nextProps)

这个函数将在每个更新生命周期中由props变化(父元素重新渲染)调用,并且将传递所有props传递的对象图,无论自上次重新渲染后props值是否已经改变亦或自前个渲染阶段。

这个函数是理想的,如果你有一个组件的状态部分取决于从父组件传递的props作为调用this.setState在这里不会导致额外的渲染调用。

请记住,由于所有props都调用了该函数,所以即使对那些没有改变的props,开发人员也会执行检查以确定实际值是否发生了变化,例如:

componentWillReceiveProps(nextProps) {
  if(nextProps.myProp !== this.props.myProps) {
    // nextProps.myProp has a different value than our current prop
    // so we can perform some calculations based on the new value
  }
}

由于使用React Fiber(16阶测试版)这个功能可能会在实际调用renderfunction之前多次调用,因此不建议在此使用任何副作用导致的操作。

DO

  • sync state to props(同步化state到props)

DON'T

  • 造成side-effects (AJAX calls etc.)

shouldComponentUpdate(nextProps, nextState, nextContext)

默认情况下,所有基于类的组件都会在它们接收到的props,其状态或环境发生变化时重新render自己。 如果重新渲染组件是计算重启(例如生成图表)或者出于某些性能原因不推荐使用,则开发人员可以访问将在update cycle中调用的特殊函数。

这个函数将在内部调用props,state和object的下一个值。 开发人员可以使用它们来验证更改是否需要重新渲染,并返回false以防止重新渲染发生。 在其他情况下,您预计会返回true。

DO

  • 用于提高perfomance

DON'T

  • 造成side-effects (AJAX calls etc.)
  • call this.setState

componentWillUpdate(nextProps, nextState)

如果shouldComponentUpdate函数未被使用,或者它决定组件在此渲染周期中应更新,则将调用另一个生命周期函数。 此函数通常用于在state的某些部分基于props时,执行状态和props同步。

在实现shouldComponentUpdate的情况下,可以使用此函数而不是componentWillReceiveProps,因为只有在实际重新渲染组件时才会调用此函数。

与所有其他componentWill *函数类似,此函数可能在渲染前最终调用多次,因此不建议在此执行导致操作的副作用。 DO

  • synchronize state to props

DON'T

  • 造成side-effects (AJAX calls etc.)

componentDidUpdate(prevProps, prevState, prevContext)

这个函数将在渲染完成后在每个重新渲染周期中被调用。 这意味着您可以确定组件及其所有子组件已正确render其自身。

由于这是唯一保证在每个重新render周期中仅被调用一次的函数,因此建议将此函数用于任何导致操作的副作用。 与componentWillUpdate和componentWillReceiveProps类似,即使这些值没有发生实际更改,也会使用以前的props,state和context的object映射来调用此函数。 因为开发人员需要手动检查给定的值是否发生了变化,然后才能执行各种更新操作:

componentDidUpdate(prevProps) {
  if(prevProps.myProps !== this.props.myProp) {
    // this.props.myProp has a different value
    // we can perform any operations that would 
    // need the new value and/or cause side-effects 
    // like AJAX calls with the new value - this.props.myProp
  }
}

DO

  • 造成side-effects (AJAX calls etc.)

DON'T

  • call this.setState 因其可致re-render

上述规则的一个例外是基于某些DOM属性更新状态,只有在组件重新呈现后才能计算该属性(例如,某些DOM节点的位置/维度)。 请注意防止更新,如果值实际上没有更改,可能会导致渲染循环。

componentDidCatch(errorString, errorInfo)

React 16中的新增功能 - 此生命周期方法的特殊之处在于它可以对发生在子组件中的事件作出反应,特别是对任何子组件中发生的任何未捕获错误。

通过这个添加,你可以让你的父母元素处理错误 - 例如 - 设置错误信息状态并在其渲染中返回适当的消息,或者登录到报告系统,例如:

componentDidCatch(errorString, errorInfo) {
  this.setState({
    error: errorString
  });
  ErrorLoggingTool.log(errorInfo);
}
render() {
  if(this.state.error) return <ShowErrorMessage error={this.state.error} />
  return (
    // render normal component output
  );
}

发生错误时,该函数将被调用:

  • errorString - 错误的.toString()消息
  • errorInfo - 具有单个字段componentStack的对象,它表示堆栈跟踪返回错误发生的位置,例如:
in Thrower
    in div (created by App)
    in App

componentDidMount

这个函数在给定组件的整个生命周期中只会被调用一次,并且它被调用表示组件及其所有子组件被正确渲染。

由于这个函数被保证只被调用一次,因此它是完成AJAX请求等引起副作用的操作的理想选择。

DO

  • 造成side-effects (AJAX calls etc.)

DON'T

  • call this.setState 因其可致re-render

上述规则的一个例外是基于某些DOM属性更新状态,只有在组件重新呈现后才能计算该属性(例如,某些DOM节点的位置/维度)。 请注意防止更新,如果值实际上没有更改,可能会导致渲染循环。

componentWillUnmount

如果它使用定时器(setTimeout,setInterval),打开套接字或执行我们需要在不再需要时关闭/移除的任何操作,则使用此函数在组件后面“清除”。

DO

  • remove any timers or listeners created in lifespan of the component

DON'T

  • call this.setState, start new listeners or timers

Component cycles

组件可能re-render有多种原因,并且每个组件中都会调用不同的函数,以便开发人员更新组件的某些部分。

  • Component creation(组件创建)

第一个循环是组件的创建,这通常是在解析的JSX🌲中第一次遇到组件时发生的:

  • Component re-rendering due to re-rendering of the parent component(由于父组件更新而组件更新)

  • Component re-rendering due to internal change (e.g. a call to this.setState())( 由于内部改变的组件更新)

  • Component re-rendering due to call to this.forceUpdate(由于call this.forceUpdate而re-render的组件)

  • Component re-rendering due to catching an error(由于捕获错误而更新组件)

在React 16中引入了“ErrorBoundaries”。 一个组件可以定义一个特殊的层,它可以捕获错误并提供一个新的生命周期方法 - componentDidCatch - 它允许开发人员提供自我修复操作以恢复或优雅地处理错误。

Shoutout to@James_k_nelson,他刚刚发布了一个componentWillReceiveProps模拟器,你可以在reactarmory.com/guides/life…找到并使用它。