003 React类组件

[A] 类组件的执行顺序及次数

        1. 类组件渲染过程中从上往下执行,先执行constructor构造器,再执行render函数返回dom

        2. constructor执行一次,即类组件首次渲染的时候执行

        3. render函数执行 1+n 次,即首次渲染的时候执行一次,当页面数据更新后再执行一次

        4. 类组件中的方法执行n次,即方法调用几次就执行几次

 

[B] 类组件的三大属性:state, props, refs

 
[C] state
  state属性中存储类组件中所定义的变量,用于动态修改html元素中的数据
    示例:

      class Weather extends React.Component {

        constructor(props) {

          super(props)

          this.state = { isHot: true}

        }

        render() {

          return <div>今天天气{this.state.isHot ? '炎热' : '凉爽'}</div>

        }

      }

      const dom = document.getElementById('box')

      ReactDOM.render(<Weather/>, dom)

  注:

    1. state存储在类的构造器中,state必须为对象

 

  state初始化

    即在类的构造器其中添加state属性

      constructor(props) {
        super(props)
        this.state = { isHot: true}
      }

  类组件事件添加

    js中添加事件的三种方式:

      1. 获取原生节点,添加事件

        const box = document.getElementById('box')
        box.onclick = () => {
          console.log('盒子被点击了')
        }

      2. 获取原生节点,添加事件监听器 addEventListener

        const box = document.getElementById('box')
        box.addEventListener('click', () => {
          console.log('盒子被点击了')
        })
      3. 给节点绑定事件
        <button onclick="btn()">按钮</button>
        function btn() {
          console.log('盒子被点击了')
        }

    React中均可以使用三种方式添加事件,但推荐使用第三种

      <button onClick={btn}>按钮</button>
      function btn() {
        console.log('盒子被点击了')
      }

      而实际上,事件的方法通常写在类的原型方法上,即:

      class Weather extends React.Component {
        constructor(props) {
          super(props)
          this.state = { isHot: true}
        }
        btn() {
          console.log(this.state.isHot)
        }
        render() {
          return <button onClick={this.btn}>按钮</button>
        }
      }
      const dom = document.getElementById('box')
      ReactDOM.render(<Weather/>, dom)

    注:

      1. 这里方法写在{}中表示内部包含的是一个js片段,而不是用""来调用的

      2. 这里是onClick而不是onclick

      3. React对事件名,属性名(onclick => onClick, class => className,... )等做了改写

      4. 为什么改写? 后续讲


    关于类方法中的this问题:

      1. 在上述添加事件方式中

        class Weather extends React.Component {
          constructor(props) {
            super(props)
            this.state = { isHot: true}
          }
          btn() {
            console.log(this.state.isHot)
          }
          render() {
            return <button onClick={this.btn}>按钮</button>
          }
        }
        ReactDOM.render(<Weather/>, document.getElementById('box'))

      2. 这里的方法调用中,会报错,因为btn中的this指向undefined

      3. 这是因为:

        onClick={this.btn}是将btn方法在堆中的地址保存到了onClick上,当点击事件发生时,会直接调用堆中的这个方法,严格模式下,直接调用方法,this就是指向undefined的

      4. 解决:

        方法一:

          在类属性中添加一个同名的方法,将类方法绑定类的this的方法赋值给这个同名方法

          constructor(props) {
            super(props)
            this.state = { isHot: true}
            this.btn = this.btn.bind(this)
          }

        方法二:

          在添加绑定事件时,通过bind方法绑定this

          <button onClick={this.btn.bind(this)}>按钮</button>

    state状态的修改:

      1. state状态的直接修改,可以实现数据修改,但react无法检测到数据变动

      2. react中提供了专门修改state状态的API,即setState

      3. setState是挂载在React.Component原型上的方法

      4. setState方法的使用:

this.setState({ isHot: !this.state.isHot })

      5. setState方法会修改传入的属性为对应的值,其他的属性不会丢失

 

    类组件的精简写法:

      原始写法:

        class Weather extends React.Component {
          constructor(props) {
            super(props)
            this.state = { isHot: true}
          }
          btn() {
            console.log(this.state.isHot)
          }
          render() {
            return <button onClick={this.btn}>按钮</button>
          }
        }
                     ReactDOM.render(<Weather/>, document.getElementById('box'))

      精简写法:

        class Weather extends React.Component {
          state = { isHot: true}
          btn = () => {
            console.log(this.state.isHot)
          }
          render() {
            return <button onClick={this.btn}>按钮</button>
          }
        }
        ReactDOM.render(<Weather/>, document.getElementById('box'))

      注意:

        1. 类中可以直接添加属性并赋值,这些直接添加的属性会添加到类的原型链上供其实例化的对象使用

          (这和方法直接写在类中是一个原理)

        2. 类中的方法也是直接写在类中的,为保证方法中的this使用正常,通常将方法携程箭头函数的形式

        3. 当类组件不需要继承来的属性时,构造器也可以省略掉

 

 

 [D] props

    props的基本使用:props主要用于父子类组件传值时,子组件接受父组件传过来的数据

      class Person extends React.Component {
        render() {
          const { name, age, sex } = this.props
          return (
            <div>
              <div>姓名:{name}</div>
              <div>年龄:{age}</div>
              <div>性别:{sex}</div>
            </div>
          )
        }
      }
      ReactDOM.render(<Person name="Carrey" age="20" sex="male"/>, document.getElementById('box'))

    注意:

      1. props属性的值获取有两种方式:

        1. constructor构造器中获取

        2. this对象自身挂在的props属性上获取

      2. 当需要传给子组件的属性较多时,可进行批量传值

        const info = {name: "Carrey" age: "20" sex: "male"}

        <Person {...info}/>

      3. 子组件中的props中获取到的属性值,是只读的,不可修改,强行修改会报错


    props添加限制:

      1. props用于父组件在调用子组件的时候给子组件传值,但有时我们需要限定有些属性必传,还要限制属性的数据类型

        对属性的数据类型限制和必传限制,我们通过 propTypes 来实现:

        先引入 propTypes 类:

          <script src="../js/prop-types.js"></script>

        在类组件中使用 propTypes 类:

          class Person extends React.Component {}
          Person.propTypes = {
            name: PropTypes.string.isRequired,
            age: PropTypes.number.isRequired,
            sex: PropTypes.string,
          }

      数据类型限制:

        字符串:PropTypes.string

        数值:PropTypes.number

        函数:PropTypes.func

      必传限制:

        PropTypes.xxx.isRequired

      注意:

        上述调用 PropTypes 的方式在react15.5版本之后才能使用,即PropTypes被封装成单独的一个类使用

        而在15.5版本以前,PropTypes是挂载在React对象上的,无需单独引入,即 PropTypes -> Reatc.PropTypes

            2. 此外,我们有时候还需要给类组件的属性添加默认值,当父组件未传时,显示该默认值

                默认值的添加,我们只需要将默认值添加在类组件对象的 defaultProps 即可:

      class Person extends React.Component {}
        Person.defaultProps = {
        name: '佚名',
        age: 100,
        sex: '未知'
      }

        props的简写:

            1. 上述例子中,我们将一个类组件的定义分成了三部分:

                类定义:

          class Person extends React.Component {}

                属性限制:

             Person.propTypes = {}

                指定默认值:

             Person.defaultProps = {}

            2. 而习惯上,我们希望将类组价的定义写在一个对象中,而不是零零散散的好几块,也就是说,上述 属性限制 和 指定默认值 两部分我们希望写在类定义的内部

                即改写为:

        class Person extends React.Component {
          static propTypes = {
            name: PropTypes.string.isRequired,
            age: PropTypes.number.isRequired,
            sex: PropTypes.string,
          }

          static defaultProps = {
            name: '佚名',
            age: 100,
            sex: '未知'
          }

          state = { msg: 'hello' }

          render() {
            const { name, age, sex } = this.props
            return (
              <div>
                <div>姓名:{name}</div>
                           <div>年龄:{age}</div>
                           <div>性别:{sex}</div>
                       </div>
                    )
                 }
             }

 

            注意:
                1. 上述代码中,propTypes和defaultProps属性前面用了关键字static进行标识
                    在类定义中:
                        直接写的属性和方法,如上述的state,render是添加在原型链上的,只能由类组件的实例调用,类本身无法调用
            var p = new Person()
             p.state         // { msg: 'hello' }
             Person.state    // undefined
                        通过static标识的属性和方法,如上述的propTypes,defaultProps是添加在类本身上的,类的实例话对象无法调用
             var p = new Person()
             p.propTypes         // undefined
             Person.propTypes    // {...}

    类中的构造器的省略与否的区别:

      1. 类中的构造器constructor可直接省略,我们也可以完成类组件定义和使用
      2. 类中的构造器不省略,则我们可以在类的构造器中做一些初始化的操作:
        1. 初始化state的值
        2. 添加绑定this的实例方法
                    constructor(props) {
                        super(props)
                        this.state = { name: 'Carrey' }
                        this.changeWeather = this.changeWeather.bind(this)
                    }
      3. 构造器中 props 参数传与不传的区别:
        传值了:则在构造器中可以直接在this上取出props对象
            constructor(props) {
                 super(props)
                  console.log(this.props)     // 正常打印
               }
        不传值:则在构造器内部无法获取到this上的props属性
          constructor() {
            super()
            console.log(this.props)     // undefined
          }
      注意:
        1. 这里主要因为没有向super()方法中传入props参数,因此this.props为undefined
        2. 修补方案是,我们可以在构造器中传入props,直接从这个props中获取属性即可
 
 
[E] refs
  简介:
    1. ref通过用来我们在js方法中通过ref属性获取DOM节点来添加事件
    2. ref有三种使用形式:
      字符串形式
      回调函数形式
      createRef形式

  字符串形式的ref

    即给元素添加一个ref属性,其值为一个自定义的字符串

    使用方法:

    class Person extends React.Component {
      print = () => {
        const { ipt } = this.refs
        console.log(ipt.value)
      }
      render() {
        return (
          <div>
            <input ref="ipt" type="text" />
            <button onClick={this.print}>打印输入值</button>
          </div>
        )
      }
    }

    即模板中绑定ref属性的元素,在this.refs上可以直接获取

    注:
      字符串形式的ref不然推荐使用,因为大量使用,会造成效率低下

        回调函数形式

    即给元素添加一个ref属性,其值为一个回调函数

    使用方法:

    class Person extends React.Component {
      print = () => {
        const { ipt } = this
        console.log(ipt.value)
      }
      render() {
        return (
          <div>
            <input ref={ c => this.ipt = c} type="text" />
            <button onClick={this.print}>打印输入值</button>
          </div>
        )
      }
    }

    即给ref绑定的回调在render渲染的时候遇到ref属性,会将其回调执行一遍,并将当前的标签元素作为参数传入

    此时,我们可以将改参数保存在组件内部,并在方法中使用

    关于回调函数执行次数的问题:

      1. ref绑定的回到函数会执行 1 + 2n 次

      2. 首次渲染的时候会执行一次,然后在每次数据更新的时候执行两遍,第一遍传入null,第二遍传入标签元素


    解决数据更新时,ref回调执行两遍的问题:

      1. 上述执行两遍是因为,回到函数直接写成行内属性的形式

      2. ref回调函数也可以绑定类组件的一个实例方法,这样数据更新时,不会再调用该回调了(零调用)

 

  createRef形式:

    使用方法:

    class Person extends React.Component {
      myRef = React.createRef()
        print = () => {
          console.log(this.myRef.current.value)
        }
      render() {
        return (
          <div>
            <input ref={ this.myRef } type="text" />
            <button onClick={this.print}>打印输入值</button>
          </div>
        )
      }
    }

 

           

    执行过程:

      1. 首先需要通过 React.createRef 方法去创建一个专门用于保存元素标签的ref容器

      2. 将该容器绑定在需要标识的标签的ref属性上

      3. 当render渲染的时候,遇到ref属性,并且发现绑定的是一个ref容器,就会将当前标签存储到这个容器中去

 
 
posted @ 2022-10-10 17:35  CarreyB  阅读(83)  评论(0编辑  收藏  举报