React生命周期

React生命周期

React的生命周期从广义上分为挂载、渲染、卸载三个阶段,在React的整个生命周期中提供很多钩子函数在生命周期的不同时刻调用。

描述

此处描述的是使用class类组件提供的生命周期函数,每个组件都包含自己的生命周期方法,通过重写这些方法,可以在运行过程中特定的阶段执行这些方法,常用的生命周期有constructor()render()componentDidMount()componentDidUpdate()componentWillUnmount()

挂载过程

当组件实例被创建并插入DOM中时,其生命周期调用顺序如下:

  • constructor()
  • static getDerivedStateFromProps()
  • render()
  • componentDidMount()

在这个阶段的componentWillMount()生命周期即将过时,在新代码中应该避免使用。

更新过程

当组件的propsstate发生变化时会触发更新,组件更新的生命周期调用顺序如下:

  • static getDerivedStateFromProps()
  • shouldComponentUpdate()
  • render()
  • getSnapshotBeforeUpdate()
  • componentDidUpdate()

在这个阶段的componentWillUpdate()componentWillReceiveProps()生命周期即将过时,在新代码中应该避免使用。

卸载过程

当组件从DOM中移除时,组件更新的生命周期调用顺序如下:

  • componentWillUnmount()

错误处理

当渲染过程,生命周期,或子组件的构造函数中抛出错误时,会调用如下方法:

  • static getDerivedStateFromError()
  • componentDidCatch()

生命周期

constructor()

React组件挂载之前,会调用它的构造函数,如果不初始化state或不进行方法绑定,则不需要为React组件实现构造函数。在为React.Component子类实现构造函数时,应在其他语句之前前调用super(props),否则this.props在构造函数中可能会出现未定义的错误。
通常在React中构造函数仅用于以下两种情况:

  • 通过给this.state赋值对象来初始化内部state
  • 为事件处理函数绑定实例。
constructor(props) {
    super(props);
}

static getDerivedStateFromProps()

getDerivedStateFromProps静态方法会在调用render方法之前调用,并且在初始挂载及后续更新时都会被调用,它应返回一个对象来更新state,如果返回null则不更新任何内容。此方法无权访问组件实例,如果确实需要,可以通过提取组件props的纯函数及class之外的状态,在getDerivedStateFromProps()和其他class方法之间重用代码。此外,不管原因是什么,都会在每次渲染前触发此方法。

static getDerivedStateFromProps(props,state) {}

render()

render()方法是class组件中唯一必须实现的方法,render()函数应该为纯函数,这意味着在不修改组件state的情况下,每次调用时都返回相同的结果,并且它不会直接与浏览器交互。如需与浏览器进行交互,请在componentDidMount()或其他生命周期方法中执行操作,保持render()为纯函数。当render被调用时,它会检查this.propsthis.state的变化并返回以下类型之一:

  • React元素,通常通过JSX创建,例如<div />会被React渲染为DOM节点,<MyComponent />会被React渲染为自定义组件,无论是<div />还是<MyComponent />均为React元素。
  • 数组或fragments,使得render方法可以返回多个元素。
  • Portals,可以渲染子节点到不同的DOM子树中。
  • 字符串或数值类型,它们在DOM中会被渲染为文本节点。
  • 布尔类型或null,什么都不渲染,主要用于支持返回test && <Child />的模式,其中test为布尔类型。
render() {}

componentDidMount()

componentDidMount()会在组件挂载后(即插入DOM树后)立即调用,依赖于DOM节点的初始化应该放在这里,如需通过网络请求获取数据,此处是实例化请求的好地方。这个方法是比较适合添加订阅的地方,如果添加了订阅,请不要忘记在componentWillUnmount()里取消订阅。
你可以在componentDidMount()里直接调用setState(),它将触发额外渲染,但此渲染会发生在浏览器更新屏幕之前,如此保证了即使在render()两次调用的情况下,用户也不会看到中间状态,请谨慎使用该模式,因为它会导致性能问题。通常应该在constructor()中初始化state,如果你的渲染依赖于DOM节点的大小或位置,比如实现modalstooltips等情况下,你可以使用此方式处理。

componentDidMount() {}

shouldComponentUpdate()

propsstate发生变化时,shouldComponentUpdate()会在渲染执行之前被调用,返回值默认为true,首次渲染或使用forceUpdate()时不会调用该方法。根据shouldComponentUpdate()的返回值,判断React组件的输出是否受当前stateprops更改的影响。默认行为是state每次发生变化组件都会重新渲染,大部分情况下,你应该遵循默认行为。
此方法仅作为性能优化的方式而存在,不要企图依靠此方法来阻止渲染,因为这可能会产生bug,你应该考虑使用内置的PureComponent组件,而不是手动编写shouldComponentUpdate()PureComponent会对propsstate进行浅层比较,并减少了跳过必要更新的可能性。
如果你一定要手动编写此函数,可以将this.propsnextProps以及this.statenextState进行比较,并返回false以告知React可以跳过更新。请注意,返回false并不会阻止子组件在state更改时重新渲染。不建议在shouldComponentUpdate()中进行深层比较或使用JSON.stringify(),这样非常影响效率,且会损害性能。目前如果shouldComponentUpdate()返回false,则不会调用UNSAFE_componentWillUpdate()render()componentDidUpdate()。后续版本React可能会将shouldComponentUpdate视为提示而不是严格的指令,并且当返回false时仍可能导致组件重新渲染。

shouldComponentUpdate(nextProps,nextState) {}

getSnapshotBeforeUpdate()

getSnapshotBeforeUpdate()在最近一次渲染输出(提交到DOM节点)之前调用,它使得组件能在发生更改之前从DOM中捕获一些信息(例如滚动位置),此生命周期的任何返回值将作为参数传递给componentDidUpdate(),该方法应返回snapshot的值或null
此用法并不常见,但它可能出现在UI处理中,如需要以特殊方式处理滚动位置的聊天线程等。

getSnapshotBeforeUpdate(prevProps,prevState) {}

componentDidUpdate()

componentDidUpdate()会在更新后会被立即调用,首次渲染不会执行此方法。当组件更新后,可以在此处对DOM进行操作,如果你对更新前后的props进行了比较,也可以选择在此处进行网络请求(例如,当props未发生变化时,则不会执行网络请求。如果shouldComponentUpdate()返回值为false,则不会调用componentDidUpdate()
你也可以在componentDidUpdate()中直接调用setState(),但请注意它必须被包裹在一个条件语句里,否则会导致死循环,因为他将无限次触发componentDidUpdate()。它还会导致额外的重新渲染,虽然用户不可见,但会影响组件性能。
如果组件实现了getSnapshotBeforeUpdate()生命周期(不常用),则它的返回值将作为componentDidUpdate()的第三个参数snapshot参数传递,否则此参数将为undefined

componentDidUpdate(prevProps,prevState,snapshot) {}

componentWillUnmount()

componentWillUnmount()会在组件卸载及销毁之前直接调用,在此方法中执行必要的清理操作,例如清除timer、取消网络请求或清除在componentDidMount()中创建的订阅等。
componentWillUnmount()中不应调用setState(),因为该组件将永远不会重新渲染,组件实例卸载后,将永远不会再挂载它。

componentWillUnmount() {}

static getDerivedStateFromError()

此生命周期会在后代组件抛出错误后被调用,它将抛出的错误作为参数,并返回一个值以更新stategetDerivedStateFromError()会在渲染阶段调用,因此不允许出现副作用,如遇此类情况,请改用componentDidCatch()

static getDerivedStateFromError(error) {}

componentDidCatch()

此生命周期在后代组件抛出错误后被调用,componentDidCatch()会在提交阶段被调用,因此允许执行副作用,它应该用于记录错误之类的情况它接收两个参数:

  • error: 抛出的错误。
  • info: 带有componentStack key的对象,其中包含有关组件引发错误的栈信息。
componentDidCatch(error,info) {}

示例

React组件的常用生命周期示例。

<!DOCTYPE html>
<html>

<head>
  <meta charset="UTF-8" />
  <title>React生命周期</title>
</head>

<body>
  <div id="root"></div>
</body>
<script src="https://unpkg.com/react@17/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script type="text/babel">

  class Clock extends React.Component {
    constructor(props) {
      super(props);
      this.state = { date: new Date() };
    }
    componentDidMount() {
      console.log("ComponentDidMount",this);
      console.log(this.props);
      console.log(this.state);
      console.log("");
      this.timer = setInterval(() => this.tick(),1000);
    }
    componentWillUnmount() {
      console.log("ComponentWillUnmount",this);
      console.log(this.props);
      console.log(this.state);
      console.log("");
      clearInterval(this.timer);
    }
    tick() {
      this.setState({ date: new Date() });
    }
    render() {
      return (
        <div>
          <h1>{this.props.tips}</h1>
          <h2>Now: {this.state.date.toLocaleTimeString()}</h2>
        </div>
      );
    }
  }

  class App extends React.Component{
    constructor(props){
      super(props);
      this.state = { 
        showClock: true,tips: "Hello World!"
      }
    }
    componentDidUpdate(prevProps,prevState) {
      console.log("ComponentDidUpdate",this);
      console.log(this.props);
      console.log(this.state);
      console.log("");
    }
    updateTips() {
      this.setState((state,props) => ({
        tips: "React update"
      }));
    }
    changeDisplayClock() {
      this.setState((state,props) => ({
        showClock: !this.state.showClock
      }));
    }
    render() {
      return (
        <div>
          {this.state.showClock && <Clock tips={this.state.tips} />}
          <button onClick={() => this.updateTips()}>更新tips</button>
          <button onClick={() => this.changeDisplayClock()}>改变显隐</button>
        </div>
      );
    }
  }

  var vm = ReactDOM.render(
    <App />,document.getElementById("root")
  );
</script>

</html>

每日一题

https://github.com/WindrunnerMax/EveryDay

参考

https://www.jianshu.com/p/b331d0e4b398
https://www.cnblogs.com/soyxiaobi/p/9559117.html
https://zh-hans.reactjs.org/docs/react-component.html
https://zh-hans.reactjs.org/docs/state-and-lifecycle.html
https://www.runoob.com/react/react-component-life-cycle.html
https://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


react 中的高阶组件主要是对于 hooks 之前的类组件来说的,如果组件之中有复用的代码,需要重新创建一个父类,父类中存储公共代码,返回子类,同时把公用属性...
我们上一节了解了组件的更新机制,但是只是停留在表层上,例如我们的 setState 函数式同步执行的,我们的事件处理直接绑定在了 dom 元素上,这些都跟 re...
我们上一节了解了 react 的虚拟 dom 的格式,如何把虚拟 dom 转为真实 dom 进行挂载。其实函数是组件和类组件也是在这个基础上包裹了一层,一个是调...
react 本身提供了克隆组件的方法,但是平时开发中可能很少使用,可能是不了解。我公司的项目就没有使用,但是在很多三方库中都有使用。本小节我们来学习下如果使用该...
mobx 是一个简单可扩展的状态管理库,中文官网链接。小编在接触 react 就一直使用 mobx 库,上手简单不复杂。
我们在平常的开发中不可避免的会有很多列表渲染逻辑,在 pc 端可以使用分页进行渲染数限制,在移动端可以使用下拉加载更多。但是对于大量的列表渲染,特别像有实时数据...
本小节开始前,我们先答复下一个同学的问题。上一小节发布后,有小伙伴后台来信问到:‘小编你只讲了类组件中怎么使用 ref,那在函数式组件中怎么使用呢?’。确实我们...
上一小节我们了解了固定高度的滚动列表实现,因为是固定高度所以容器总高度和每个元素的 size、offset 很容易得到,这种场景也适合我们常见的大部分场景,例如...
上一小节我们处理了 setState 的批量更新机制,但是我们有两个遗漏点,一个是源码中的 setState 可以传入函数,同时 setState 可以传入第二...
我们知道 react 进行页面渲染或者刷新的时候,会从根节点到子节点全部执行一遍,即使子组件中没有状态的改变,也会执行。这就造成了性能不必要的浪费。之前我们了解...
在平时工作中的某些场景下,你可能想在整个组件树中传递数据,但却不想手动地通过 props 属性在每一层传递属性,contextAPI 应用而生。
楼主最近入职新单位了,恰好新单位使用的技术栈是 react,因为之前一直进行的是 vue2/vue3 和小程序开发,对于这些技术栈实现机制也有一些了解,最少面试...
我们上一节了了解了函数式组件和类组件的处理方式,本质就是处理基于 babel 处理后的 type 类型,最后还是要处理虚拟 dom。本小节我们学习下组件的更新机...
前面几节我们学习了解了 react 的渲染机制和生命周期,本节我们正式进入基本面试必考的核心地带 -- diff 算法,了解如何优化和复用 dom 操作的,还有...
我们在之前已经学习过 react 生命周期,但是在 16 版本中 will 类的生命周期进行了废除,虽然依然可以用,但是需要加上 UNSAFE 开头,表示是不安...
上一小节我们学习了 react 中类组件的优化方式,对于 hooks 为主流的函数式编程,react 也提供了优化方式 memo 方法,本小节我们来了解下它的用...
开源不易,感谢你的支持,❤ star me if you like concent ^_^
hel-micro,模块联邦sdk化,免构建、热更新、工具链无关的微模块方案 ,欢迎关注与了解
本文主题围绕concent的setup和react的五把钩子来展开,既然提到了setup就离不开composition api这个关键词,准确的说setup是由...
ReactsetState的执行是异步还是同步官方文档是这么说的setState()doesnotalwaysimmediatelyupdatethecomponent.Itmaybatchordefertheupdateuntillater.Thismakesreadingthis.staterightaftercallingsetState()apotentialpitfall.Instead,usecom