React:Class 组件详解

3,933 阅读4分钟

创建方式

两种方式创建 Class 组件

ES5方式(过时)

import React from 'react'

const A = React.createClass({
    render() {
        return(
            <div>hi</div>
        )
    }
})
export default A
// 由于 ES5 不支持 class,才会有这种方式

ES6方式

import React from 'raeact'
​
class B extends React.Component {
    constructor(props){
        super(props)
    }
    render(){
        return(
            <div>hi</div>
        )
    }
}
export default B

对比

ES6 的 class 方式更好

如果浏览器不支持ES6(IE8),用webpack+babel将ES6翻译为ES5即可

Props 外部数据

传入props给B组件

class Parent extends React.Component {
    constructor(props){
        super(props)
        this.state = {name: 'river'}
    }
    onClick = () => {}
    render(){
        return <B name={this.state.name}
                  onClick={this.onClick}>hi</B>
    }
}
// 外部数据被包装为一个对象
// {name: 'river', onClick:..., children: 'hi'}
// 此处的 onClick 是一个回调

初始化

class B extends React.Component {
    constructor(props){
        super(props);
    }
    render(){}
}

要么不初始化,即不写constructor

要么初始化,且写全套

效果:这么做之后,this.props就是外部数据对象的地址

读取props

class B extends React.Component {
    constructor(props) {
        super(props);
    }
    render(){
        return <div onClick={this.props.onClick}>
            {this.props.name}
            <div>
                {this.props.children}    
            </div>
        </div>
    }
}
// 通过 this.props.xxx 读取

写props

改props的值(一个地址)

this.props = {/*另一个对象*/}

注意:这样写的代码没有意义,既然是外部数据,就应该由外部更新

改props的属性

this.props.xxx='hi'

注意:这样写的代码没有意义,既然是外部数据,就应该由外部更新

原则

应该由数据的主人对数据进行更改

相关钩子

componentWillReceiveProps 钩子

当组件接受新的props时,会触发此钩子

钩子即“特殊函数”

示例

该钩子已经被弃用

更名为 UNSAFE_componentWillReceiveProps

Props的作用

接受外部数据

只能读不能写,外部数据由父组件传递

接受外部函数

在恰当的时机调用该函数,该函数一般是父组件的函数

State & setState

初始化State

class B extends React.Component{
  constructor(props){
    super(props);
    this.state = {
      user: {name: 'river', age: 18}
    }
  }
  render() {/* ... */}
}

读写State

读用 this.state

this.state.xxx.yyy.zzz

写用 this.setState(???,fn)

示例

this.setState(newState,fn)
// 注意 setState 不会立刻改变 this.state,会在当前代码运行完成后,再去更新 this.state,从而触发 UI 更新
this.setState((state,props)=> newState,fn)
// 这种方式的 state 反而更易于理解
// fn 会在写入成功后执行

写时会 shallow merge

setState会自动将新state旧state进行一级合并

直接修改 this.state 的属性值

this.state n += 1
this.setState(this.state)

可以实现,但不推荐使用

生命周期 Lifecycle

生命周期

类比如下代码

let div = document.createElement('div')
// 这是 div 的create / construct 过程
div.textContent = 'hi'
// 这是初始化 state
document.body.appendChild(div)
// 这是 div 的 mount 过程
div.textContent = 'hi2'
// 这是 div 的 update 过程
div.remove()
// 这是 div 的 unmount 过程

同理

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

函数列表

  • constructor() 在这里初始化 state
  • static getDerivedStateFromProps()
  • shouldComponentUpdate() return true / false 更新 / 阻止更新
  • render() 创建虚拟DOM
  • getSnapshotBeforeUpdate()
  • componentDidMount() 组件已出现在页面
  • componentDidUpdate() 组件已更新
  • componentDidUnmount() 组件将死
  • static getDerivedStateFromError()
  • componentDidCatch()

constructor

用途:

  • 初始化props

  • 初始化state,但此时不能调用setState

  • 用来写bind this

    constructor(){
      /* 其他代码略 */
      this.onClick = this.onClick.bind(this)
    }
    // 可以用新语法代替
    onClick = () => {}
    constructor(){ /*其他代码略*/ }
    
  • 可不写

shouldComponentUpdate

用途:

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

  • 返回 false 表示阻止 UI 更新

  • 示例

    由上面示例得到一些启发——可以将newStatethis.state的每个属性都对比一下。如果全都相等,就不更新,如果一个不等,就更新。

    实际上 React 内置了这个功能,示例

    React.PureComponent会在 render 之前对比新 state 和旧 state 的每一个 key,以及新 props 和旧 props 的每一个 key。

    如果所有 key 的值全都一样,就不会 render;如果有任何一个 key 的值不同,就会 render。

    注意这个对比只是一层(浅对比)。

render

用途:

  • 展示视图

    return (<div>...</div>)

  • 只能有一个根元素

  • 如果有两个根元素,就要用<React.Fragment>内容</React.Fragment>,可以缩写成<></>

示例

使用<React.Fragment/><></>,相当于一个占位符,渲染后就不显示了

image-20210728125835530

而如果用div></div>包裹,这层div是一直显示的

image-20210728130108912

技巧:

示例

  • render 里面可以写 if...else
  • render 里面可以写?:表达式
  • render 里面可以写 &&||表达式
  • render 里面不能直接写for循环,需要用数组
  • render 里面可以写array.map(循环)

componentDidMount()

用途:

  • 在元素插入页面后执行代码,这些代码依赖DOM

    比如像获取div的宽度,就最好在这里写

  • 此处可以发起加载数据AJAX请求(官方推荐)

  • 首次渲染会执行此钩子

示例

关于上面代码中用到的Ref

componentDidUpdate()

用途:

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

参数:

componentDidUpdate(prevProps, prevState, snapshot)

componentWillUnmount

用途:

  • 组件将要被移出页面然后被销毁时执行代码
  • unmount过的组件不会再次mount

举例:

如果在c..DidMount里面监听了window.scroll,那么就要在c..WillUnmount里面取消监听

如果在c..DidMount里面创建了Timer,那么就要在c..WillUnmount里面取消Timer

如果在c..DidMount里面创建了AJAX请求,那么就要在c..WillUnmount里面取消请求

分阶段看钩子执行顺序

image-20210728234503781.png