第十三章 React生命周期(新)

简介: 第十三章 React生命周期(新)

在新版本的生命周期中废弃了3个钩子函数,新增了2个钩子函数:image.png

从上图可知,新增的2个钩子函数是我们从来没有见过的:

  • getDerivedStateFromProps
  • getSnapshotBeforeUpdate

废弃的3个钩子函数是:

  • componentWillMount
  • componentWillUpdate
  • componentWillReceiveProps

根据官方文档说明:在React17版本中这废弃的3个钩子函数被重命名了,需要加一个前缀UNSAFE_才能使用。但是在18.x版本中就会彻底删除这3个钩子函数。以下是你在新版的React中继续使用这3个钩子函数时报的警告:

警告:componentWillMount已重命名,不建议使用。详情见https://reactjs.org/link/unsafe-component-lifecycles。
移动带有副作用的代码到componentDidMount,并在构造函数中设置初始状态。
将componentWillMount重命名为UNSAFE_componentWillMount以在非严格模式下抑制此警告。在React 18.x,只有UNSAFE_名称将工作。要将所有已弃用的生命周期重命名为它们的新名称,您可以在项目源文件夹中运行' npx react-codemod rename-unsafe-lifecycles '。

使用getDerivedStateFromProps

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>生命周期Count</title>
</head>
<body>
  <!-- 准备好员工“容器” -->
  <div id="app"></div>
  <!-- 引入ReactJS核心库 -->
  <script type="text/javascript" src="../JS/新版本/react.development.js"></script>
  <!-- 引入React-DOM核心库,用于操作DOM -->
  <script type="text/javascript" src="../JS/新版本/react-dom.development.js"></script>
  <!-- 引入Babel,用于编译jsx为js -->
  <script type="text/javascript" src="../JS/新版本/babel.min.js"></script>
  <!-- 此处类型为babel -->
  <script type="text/babel">
    class Count extends React.Component {
      constructor(props){
        console.log('Count--constructor')
        super(props)
        // 初始化状态
        this.state = { count: 0 }
      }
      // 卸载组件
      death = () => {
        ReactDOM.unmountComponentAtNode(document.getElementById('app'))
      }
      // 求和加1
      add = () => {
        let { count } = this.state
        count += 1
        this.setState({count})
      }
      // 强制更新
      force = () => {
        this.forceUpdate()
      }
      getDerivedStateFromProps(){
        console.log("Count--getDerivedStateFromProps")
      }
      // 组件挂载完毕
      componentDidMount() {
        console.log("Count--componentDidMount")
      }
      // 判断组件是否需要更新,返回一个false或者true
      shouldComponentUpdate(){
        console.log("Count--shouldComponentUpdate")
        return true
      }
      // 组件更新完毕
      componentDidUpdate(){
        console.log("Count--componentDidUpdate")
      }
      // 组件将要被卸载
      componentWillUnmount(){
        console.log("Count--componentWillUnmount")
      }
      // 渲染DOM结构
      render () {
        console.log("Count--render")
        const {count} = this.state
        return (
          <div>
            <h1>当前求和为:{count}</h1>
            <button onClick={this.add}>点我+1</button>
            <button onClick={this.force}>强制更新</button>
            <button onClick={this.death}>卸载组件</button>
          </div>
        )
      }
    }
    // 2、将虚拟DOM渲染到页面,标签必须闭合
    ReactDOM.render(<Count />, document.getElementById('app'))
  </script>
</body>
</html>

我们还是使用Count组件作为案例使用。但是报错了:

Warning: Count: getDerivedStateFromProps() is defined as an instance method and will be ignored. Instead, declare it as a static method.

大概意思是:警告:Count: getDerivedStateFromProps()被定义为实例方法,将被忽略。相反,应该将其声明为静态方法。

意思是这个一个静态方法,我们需要加一个关键字:static

static getDerivedStateFromProps(){
        console.log("Count--getDerivedStateFromProps")
      }

但是又有了一个新的错误:

Warning: Count.getDerivedStateFromProps(): A valid state object (or null) must be returned. You have returned undefined.

大概意思是:Count.getDerivedStateFromProps():必须返回一个有效的状态对象(或null)。您已经返回undefined

这个错误是说我们要返回一个和state对象或者是null,只有这2种选择,而不是undefined

static getDerivedStateFromProps(){
        console.log("Count--getDerivedStateFromProps")
        return null
      }

这次我们返回null试试水,发现没有报错,且打印出了生命周期顺序:

Count--constructor
Count--getDerivedStateFromProps
Count--render
Count--componentDidMount

那什么又是返回一个状态对象呢?我们看看官网:

static getDerivedStateFromProps()

static getDerivedStateFromProps(props, state)

getDerivedStateFromProps 会在调用 render 方法之前调用,并且在初始挂载及后续更新时都会被调用。它应返回一个对象来更新 state,如果返回 null 则不更新任何内容。

此方法适用于罕见的用例,即 state 的值在任何时候都取决于 props。例如,实现  组件可能很方便,该组件会比较当前组件与下一组件,以决定针对哪些组件进行转场动画。

派生状态会导致代码冗余,并使组件难以维护。 确保你已熟悉这些简单的替代方案:

此方法无权访问组件实例。如果你需要,可以通过提取组件 props 的纯函数及 class 之外的状态,在getDerivedStateFromProps()和其他 class 方法之间重用代码。

请注意,不管原因是什么,都会在每次渲染前触发此方法。这与 UNSAFE_componentWillReceiveProps 形成对比,后者仅在父组件重新渲染时触发,而不是在内部调用 setState 时。

我们发现一句话非常重要:此方法适用于罕见的用例,即 state 的值在任何时候都取决于 props。

我们以此来修改代码:

static getDerivedStateFromProps(props,state){
        console.log("Count--getDerivedStateFromProps",props,state)
        return props
      }
//...
ReactDOM.render(<Count count={110} />, document.getElementById('app'))

我们在标签属性上面写了一个count={110},并在该函数中返回props,此时我们发现页面中显示的内容是:110,且点击自增按钮,页面不发生任何改变。但是这个函数我们使用的极少,就此作为了解。

使用getSnapshotBeforeUpdate

 getSnapshotBeforeUpdate(){
        console.log("Count--getSnapshotBeforeUpdate")
      }

此时我们更新组件时报了一个错误:

Warning: Count.getSnapshotBeforeUpdate(): A snapshot value (or null) must be returned. You have returned undefined.

大概意思是:警告:Count.getSnapshotBeforeUpdate():必须返回快照值(或null)。您已经返回undefined

和之前错误类似,同样我们返回null试试水:

 getSnapshotBeforeUpdate(){
        console.log("Count--getSnapshotBeforeUpdate")
        return null
      }

此时我们更新组件不报错,且打印出了执行的生命周期:

Count--getDerivedStateFromProps {count: 110} {count: 2}
Count--shouldComponentUpdate
Count--render
Count--getSnapshotBeforeUpdate
Count--componentDidUpdate

同样的问题:什么是返回一个快照值?我们同样看看官网:

getSnapshotBeforeUpdate()

getSnapshotBeforeUpdate(prevProps, prevState)

getSnapshotBeforeUpdate() 在最近一次渲染输出(提交到 DOM 节点)之前调用。它使得组件能在发生更改之前从 DOM 中捕获一些信息(例如,滚动位置)。此生命周期的任何返回值将作为参数传递给 componentDidUpdate()。

此用法并不常见,但它可能出现在 UI 处理中,如需要以特殊方式处理滚动位置的聊天线程等。

应返回 snapshot 的值(或 null)。

意思是它能在组件即将渲染到页面之前,我们可以通过它拿到一些DOM信息,并传递给componentDidUpdate()使用。

我们写一个案例说明:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>测试生命周期函数getSnapshotBeforeUpdate</title>
  <style>
    .list {
      width: 200px;
      height: 150px;
      background-color: pink;
      overflow: auto;
    }
    .news {
      width: 100%;
      height: 30px;
    }
  </style>
</head>
<body>
  <!-- 准备好员工“容器” -->
  <div id="app"></div>
  <!-- 引入ReactJS核心库 -->
  <script type="text/javascript" src="../JS/新版本/react.development.js"></script>
  <!-- 引入React-DOM核心库,用于操作DOM -->
  <script type="text/javascript" src="../JS/新版本/react-dom.development.js"></script>
  <!-- 引入Babel,用于编译jsx为js -->
  <script type="text/javascript" src="../JS/新版本/babel.min.js"></script>
  <!-- 此处类型为babel -->
  <script type="text/babel">
    class Count extends React.Component {
      listRef = React.createRef()
      state = { newArr: [] }
      getSnapshotBeforeUpdate () {
        return this.listRef.current.scrollHeight
      }
      // 组件更新完毕
      componentDidUpdate (prevProps,prevState,snapshotValue) {
        console.log("Count--componentDidUpdate",snapshotValue)
        this.listRef.current.scrollTop += this.listRef.current.scrollHeight - snapshotValue
      }
      // 组件挂载完毕
      componentDidMount () {
        setInterval(() => {
          let { newArr } = this.state
          const news = "新闻" + (newArr.length + 1)
          newArr = [news, ...newArr]
          this.setState({ newArr })
        }, 1000)
      }
      // 渲染DOM结构
      render () {
        const { newArr } = this.state
        return (
          <div className="list" ref={this.listRef}>
            {
              newArr.map((val, idx) => {
                return <div className="news" key={idx}>{val}</div>
              })
            }
          </div>
        )
      }
    }
    // 2、将虚拟DOM渲染到页面,标签必须闭合
    ReactDOM.render(<Count count={110} />, document.getElementById('app'))
  </script>
</body>
</html>

以上组件的效果如下:

image.png

就是让滚动条一直处于底部,让最初的新闻信息一直停留在当前可见区域。大概主要的代码逻辑如下:

 // 组件挂载完毕
      componentDidMount () {
        setInterval(() => {
          let { newArr } = this.state
          const news = "新闻" + (newArr.length + 1)
          newArr = [news, ...newArr]
          this.setState({ newArr })
        }, 1000)
      }

设置定时器,每个一秒就增加一条新闻信息。

getSnapshotBeforeUpdate () {
    return this.listRef.current.scrollHeight
}

在组件更新快要完成时,获取组件的滚动条高度。

 // 组件更新完毕
 componentDidUpdate (prevProps,prevState,snapshotValue) {
     console.log("Count--componentDidUpdate",snapshotValue)
     this.listRef.current.scrollTop += this.listRef.current.scrollHeight - snapshotValue
 }

在组件更新完成时,设置滚动条的scrollTop属性,使得内容一直在可见区域。

小总结

  • 新的生命周期的三个阶段

1、初始化阶段:由ReactDOM.render()触发—初次渲染

  • 1===>constructor()
  • 2===>getDerivedStateFromProps()
  • 3===>render()
  • 4===>componentDidMount()

2、更新阶段:由组件内部this.setState()或者父组件render触发

  • 1===>getDerivedStateFromProps
  • 2===>shouldComponentUpdate()
  • 3===>render()
  • 4===>getSnapshotBeforeUpdate()
  • 5===>componentDidUpdate()

3、卸载组件:由ReactDOM.unmountComponentAtNode()触发

  • 1===> componentWillUnmount()
  • 重要的钩子

1、render: 初始化渲染或更新渲染调用

2、componentDidMount: 开启监听,发送ajax请求

3、componentWillUnmount:做一些收尾的工作,如:销毁监听,清理定时器

  • 即将废弃的钩子

1、componentWillMount

2、componentWillUpdate

3、componentWillReceiveProps

17.x版本使用需要加上UNSAFE_前缀使用,18.x版本彻底废弃,不建议使用。

相关文章
|
1月前
|
前端开发 JavaScript
React 组件生命周期
React 组件生命周期
29 0
|
17天前
|
前端开发 JavaScript
react 组件的生命周期
React组件的生命周期包括从创建到销毁的各个阶段,如挂载(mounting)、更新(updating)和卸载(unmounting)。每个阶段都有特定的方法,用于控制组件的行为和状态,确保高效、有序地渲染和管理UI。
|
2月前
|
前端开发 JavaScript 开发者
介绍一下React生命周期
介绍一下React生命周期
98 9
|
1月前
|
存储 前端开发 JavaScript
深入理解React组件的生命周期与Hooks
【10月更文挑战第7天】深入理解React组件的生命周期与Hooks
84 0
|
3月前
|
前端开发 JavaScript
React的生命周期演示-新(12)
【8月更文挑战第15天】React的生命周期演示-新
49 5
React的生命周期演示-新(12)
|
3月前
|
前端开发 JavaScript
React的生命周期简介(十)
【8月更文挑战第15天】React的生命周期简介
48 2
React的生命周期简介(十)
|
2月前
|
前端开发 API UED
React组件生命周期详解
【9月更文挑战第4天】在React应用开发中,掌握组件生命周期对于管理状态和属性至关重要,并能有效提升应用性能。本文详细介绍了React组件生命周期的三个阶段:挂载、更新和卸载,并通过代码示例展示了如何避免状态更新导致的死循环及优化网络请求等问题,帮助开发者构建更高效、可维护的应用。
80 2
|
3月前
|
前端开发 JavaScript
React 组件的生命周期阶段详解
【8月更文挑战第30天】
48 7
|
3月前
|
前端开发 JavaScript
React 组件生命周期方法详解
【8月更文挑战第30天】
48 5
|
3月前
|
前端开发 JavaScript
React的生命周期演示-旧(11)
【8月更文挑战第15天】React的生命周期演示-旧(11)
33 3