Class组件详解

两种方式创建Class组件

ES5方式(过时)

    import React from 'react'

    const Demo = React.createClass({
      render(){
        return(<div>hello lucidity</div>)
      }
    })

      export default Demo

ES6方式(推荐使用)

import React from 'react'                 1

class Demo extends  React.Component{      2
   constructor(props){                    3
     super(props)                         4
   }                                      5
   render(){
      return(<div>hello lucidity</div>)
    }
 }
 export default Demo

第3,4,5行代码可写可不写,当这三行代码中没有其它的代码时可不写,反之则要写

以上都是在项目中单文件组件的书写方式,在学习中还是使用CDN的引入方式

Class中props的使用(外部数据)

props的简单实用

    class App extends React.Component {
      render() {
        return (
          <div>hello {this.props.name}</div>
        );
      }
    }

    const rootElement = document.getElementById("app")
    ReactDOM.render(<App name="frank" />, rootElement);

输出:hello frank

props的批量传递

    class App extends React.Component {
      render() {
        const {name,age,sex} = this.props   使用解构赋值拿出name,age,sex
        return (
            <ul>
              <li>{name}</li>
              <li>{age}</li>
              <li>{sex}</li>
            </ul>
        );
      }
    }
    const demo = {name: '坤坤',age: 28,sex: '男'}     定义demo等于这些对象
    const rootElement = document.getElementById("app")
    ReactDOM.render(<App {...demo} />, rootElement)   直接用{...demo}表示demo

页面中呈现的内容是:


image.png

对props进行限制

还是上面那段代码,只不过要对props进行限制:

  • name是必传的,且为字符串
  • age可传可不传,不传的话默认为18
  • sex可传可不传,不传的话默认为男

要想限制props,需要引入prop-types这个库,这个库的就是对组件标签属性进行限制

CDN引入:<script src="https://cdn.bootcdn.net/ajax/libs/prop-types/15.7.2/prop-types.js"></script>

propTypes要写在render函数的上面:

      static propTypes = {
      }

name是必传的,且为字符串,代码为

name: PropTypes.string.isRequired

age可传可不传,不传的话默认为18,代码为

    age: PropTypes.number,
    sex: PropTypes.string

代码为

      static propTypes = {
        name: PropTypes.string.isRequired,
        age: PropTypes.number,
        sex: PropTypes.string
      }

通过propTypes,我们确实可以对外部数据进行限制,可是却无法设置默认值,想要设置默认值,需要用到dafaultProps,这个不再需要引入,因为在引入React的时候已经引入了

如何使用dafaultProps

static dafaultProps = {
}

对age设置默认值

age: 18

对sex设置默认值

sex: '男'

代码为

    static defaultProps = {
      age: 18,
      sex: '男'
    }

完整代码为

<!DOCTYPE html>
<html>
  <head>
    <title>Parcel Sandbox</title>
    <meta charset="UTF-8" />
  </head>
  <body>
    <div id="app"></div>
    <script src="https://cdn.bootcdn.net/ajax/libs/react/17.0.1/umd/react.development.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/react-dom/17.0.1/umd/react-dom.development.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/babel-standalone/7.0.0-beta.3/babel.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/prop-types/15.7.2/prop-types.js"></script> 
    <script type="text/babel">   
      class App extends React.Component {
        static propTypes = {
          name: PropTypes.string.isRequired,
          age: PropTypes.number,
          sex: PropTypes.string
        }
        static defaultProps = {
          age: 18,
          sex: '男'
        }
        render() {
          const {name,age,sex} = this.props
          return (
            <div>
              <ul>
                <li>{name}</li>
                <li>{age}</li>
                <li>{sex}</li>
              </ul>
            </div>
          );
        }
      }
      const demo = {name: 'lucidity',age: 28,sex: '男'}
      const rootElement = document.getElementById("app")
      ReactDOM.render(<App {...demo} />, rootElement);
    </script>
  </body>
</html>

页面中显示为

image.png
注意:props只支持读,不支持写,父组件传给子组件的东西,应该由父组件自己修改,而不是通过子组件来修改

Class中state的使用(内部数据)

对state进行初始化

        constructor(props){
          super(props)
          this.state = {
            n: 0         初始化n为0
          }
        }

修改state的值用setState

        add = () => {
          this.setState((state)=> ({n: this.state.n + 1}))
        }

点击button+1的案例

<!DOCTYPE html>
<html>
  <head>
    <title>Parcel Sandbox</title>
    <meta charset="UTF-8" />
  </head>
  <body>
    <div id="app"></div>
    <script src="https://cdn.bootcdn.net/ajax/libs/react/17.0.1/umd/react.development.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/react-dom/17.0.1/umd/react-dom.development.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/babel-standalone/7.0.0-beta.3/babel.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/prop-types/15.7.2/prop-types.js"></script> 
    <script type="text/babel">   
      class App extends React.Component {
        constructor(props){
          super(props)
          this.state = {
            n: 0
          }
        }
        add = () => {
          this.setState((state)=> ({n: state.n + 1}))
        }
        render() {
          return (
            <div>
              {this.state.n}
              <button onClick = {this.add}>+1</button>
            </div>
          );
        }
      }
      const rootElement = document.getElementById("app")
      ReactDOM.render(<App/>, rootElement);
    </script>
  </body>
</html>

生命周期

类比如下代码

let div = document.createElement('div')

  • 这是div的create(创建)过程
    div textContent = 'hi'
  • 这是初始化state
    document.body.appendChild(div)
  • 这是div的mount(挂载)过程
    div textContent = 'hello'
  • 这是div的update(更新)的过程
    div.remove()
  • 这是div的卸载过程

同理,react组件也有这些过程,我们称之为生命周期

函数列表

因为在创建或者更新组件的时候,会触发某些函数,而这些函数被叫做生命周期

constructor() 初始化state
shouldComponentUpdate() return false 阻止更新
render() 创建虚拟DOM
componentDidMount 组件已经出现在页面
componentDidUpdate 组件已更新
componentDidUnmount 组件将被卸载

constructor()

作用:

  • 初始化props,
  • 初始化state,但此时并不能调用setState
  • 用来写bind(绑定) this
constructor() {
/*.......*/
this.onClick = this.onClick.bind(this)
}

绑定this,或者用下面的新语法箭头函数代替
onClick = () => {}

shouldComponentUpdate()

是否应该更新组件

用途:

  • 返回true表示不阻止UI更新
  • 返回false表示阻止UI更新

为什么要阻止UI更新呢?首先我们看一下vue和react的区别,分别用vue和react实现点击button+1的函数(n都为0)
vue:

    add(){
      this.n += 1 
    }

react:

 add = () => {
    this.setState(state=> ({n: state.n + 1}))
 }

vue:点击之后,0+1 = 1,之前0的对象的内存地址 === 现在1的对象的内存地址
react:点击之后,0+1 = 1,之前0的对象的内存地址 !== 现在1的对象的内存地址,
一句话总结,vue+1操作之后,不会产生新的对象,而react+1之后,会产生新的对象

明白vue和react的这个区别以后,看下面的代码

<!DOCTYPE html>
<html>
  <head>
    <title>Parcel Sandbox</title>
    <meta charset="UTF-8" />
  </head>
  <body>
    <div id="app"></div>
    <script src="https://cdn.bootcdn.net/ajax/libs/react/17.0.1/umd/react.development.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/react-dom/17.0.1/umd/react-dom.development.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/babel-standalone/7.0.0-beta.3/babel.min.js"></script>
    <script type="text/babel">   
      class App extends React.Component {
        constructor(props){
          super(props)
          this.state = {
            n: 0
          }
        }
        add = () => {
          this.setState(state=> ({n: state.n + 1}))
          this.setState(state=> ({n: state.n - 1}))
        }

        render() {
          return (
            <div>
              {this.state.n}
              <button onClick = {this.add}>+1  -1</button>
            </div>
          );
        }
      }
      const rootElement = document.getElementById("app")
      ReactDOM.render(<App/>, rootElement);
    </script>
  </body>
</html>

n为0,当点击button的时候,调用add函数,先+1,再-1,还是原来的值0,但是还会产生新的对象。值还是原来的值,但却返回了新的对象,react感觉这样不好,于是就有了shouldComponentUpdate()函数,shouldComponentUpdate()会检查旧的值是否等于新的值,如果改变了就更新UI,不改变就阻止更新UI。

render的上面添上下面几行代码

        shouldComponentUpdate(newProps, newState){
          if(newState.n === this.state.n) {        如果新的值 === 旧的值
            return false                           则不更新UI
          }else {
            return true                            反之则更新
          }
        }

此时,便成功解决了上面的问题,可以打印一下看看,在render中打印console.log('n的值变了'),没有加上面这段代码,点一次就会打印一次,而加了上面的代码,只会打印一次(打印一次是因为初始化)

其实还有一种方法也可以实现上面的功能,将class App extends React.Component改为class App extends React.PureComponent

render()

用途:展示视图

注意:

  • 只有一个根元素
  • 非要有两个根元素,用<React.Fragment />包起来
  • render()可以写if--else,三元表达式,可以写arraymapfilterreduce
  • 不能写for循环
ComponentDidMount()

组件已经挂载

用途:

  • 在元素插入页面后执行代码,这些代码依赖DOM
  • 此处可以发起加载数据的AJAX请求
  • 首次渲染会执行此钩子

举个栗子:获取div的高度height

<!DOCTYPE html>
<html>
  <head>
    <title>Parcel Sandbox</title>
    <meta charset="UTF-8" />
    <style>
      div {
        height: 40px;      一开始用css设置高为40px
      }
    </style>
  </head>
  <body>
    <div id="app"></div>
    <script src="https://cdn.bootcdn.net/ajax/libs/react/17.0.1/umd/react.development.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/react-dom/17.0.1/umd/react-dom.development.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/babel-standalone/7.0.0-beta.3/babel.min.js"></script>
    <!-- <script src="https://cdn.bootcdn.net/ajax/libs/prop-types/15.7.2/prop-types.js"></script>  -->
    <script type="text/babel">   
      class App extends React.Component {
        constructor(props){
          super(props)
          this.state = {
            height: undefined      初始化height为undefined
          }
          this.myRef = React.createRef()          用createRef() API来标记 
        }
        componentDidMount(){
          const div = this.myRef.current      获取div
          const height = div.getBoundingClientRect().height     拿到div的高
          this.setState({height: height})     设置state中的高度为上一行获取的高度
        }
        render(){
          return (
            <div ref={this.myRef}>hello lucidity {this.state.height}px</div>
          )
        }
      }
      const rootElement = document.getElementById("app")
      ReactDOM.render(<App/>, rootElement);
    </script>
  </body>
</html>

下图是页面中呈现的效果


image.png

componentDidUpdate()

组件已经更新

用途:

  • 在视图更新后执行代码
  • 此处也可以发起AJAX请求,用来更新数据
  • 首次渲染不会执行此钩子
  • 此处setState可能会引起无限循环,除非放在if里面
  • 假如shouldComponentUpdate()返回false,则不会触发此钩子

componentWillUnmount

组件将被销毁时

用途:

  • 组件将要被移除页面然后被销毁时执行代码
  • unmount(销毁)过的组件不会再次mount(创建,挂载)

举例:
如果你在ComponentDidMount()里面创建了请求,那么你就要在componentWillUnmount取消请求

如果你在ComponentDidMount()里面创建了timer(定时器),那么你就要在componentWillUnmount清除定时器,

因为组件都要被销毁了,创建的东西或者请求的东西都没有意义了

总结:分阶段看钩子的执行顺序

首次渲染:constructor()---------render()---------更新UI---------componentDidMount

再次渲染:(props,setState变了)---------shouldComponentUpdate---------(判断返回true或false,true结束,false继续)---------render()---------更新UI---------componentDidUpdate

销毁:componentWillUnmount

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容