Class组件由浅入深

211 阅读3分钟
# Class组件详解

创建Class组件

两种方式创建class组件

ES5方式(过时)


import React from 'react'

const A = React.createClass({
    render() {
        return (
            <div>hi</div>
        )
    }
})

exoprt default A

ES6方式


import React from 'react'

//建议能用React.PureComponent代替React.Component
class B extends React.Component{
    constructor(props){
        super(props)
    }
    render(){
        return(
            <div>hi</div>
        )
    }
}

export default B

Props(外部数据)

传入props给子组件

import React from 'react'

import ReactDOM from 'react-dom'


class Parent extends React.Component{

constructor(props){

super(props)

this.state = {name: 'jason'}

    }

onClick = () =>{}

render(){

return (

<B name={this.state.name}

onClick={this.onClick}

>hi</B>

        )
    }
}

//外部被包装为一个对象
//{name:'jason',onClick:...,childer:'hi'}
//此处onClick是回调

Props外部数据初始化

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

//this.props就是外部数据对象的地址了

Props外部数据读取


//通过this.props.xxx读取
//建议永远不要对props进行改写
class B extends React.Component{
    constructor(){
        super()
    }
    render(){
        return(
            <div onClick = {this.props.onClick}>
                {this.props.name}
                <div>
                    {this.props.children}
                </div>
            </div>
        )
    }
}

Props的作用

  • 接受外部数据,只能读不能写,外部数据由父组件传递
  • 接受外部函数,在恰当的时机调用该函数,该函数一般是父组件的函数

State(内部数据)

初始化State

class B extends React.Component{
    constructor(){
        super()
        //初始化state
        this.state = {
            user: {
                name: 'jason'
                age: 18
            }
        }
    }
}

读写state

读用this.state

this.state.xxx

写用 this.setState(???,fn),(注意:setState不会立即改变this.state,会在当前代码运行完后,再去更新this.state,从而触发UI更新)

//bug更少的写法
this.setState((state,props) => newState,fn) //fn会在写入成功后执行

写是会 shallow merge setState会自动将新state与旧state进行一级合并

生命周期

生命周期钩子执行顺序图:

image.png

constructor()    //在这里初始化state
shouldComponentUpdate()    //return false阻止更新
render()    //创建虚拟DOM
componentDidMount()    //组件已出现在页面
componentDidUpdate()    //组件已更新
componentWillUnmount()    //组将将亡

constructor

用途:

  • 初始化props
  • 初始化state,但此时不能调用setState
  • 用来bind this
consttructor(){
    /*...*/
    this.onClick = this.onClick.bind(this)
}
//新语法
onClick = () =>{}
constructor(){/*....*/}
  • 可不写

shouldComponentUpdate

用途:

  • 返回true表示不阻止UI更新
  • 返回false表示阻止UI更新
class App extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      n: 1
    }
  }
  onClick = () => {
    this.setState((state) => ({
      n: state.n + 1
    }))
    this.setState((state) => ({
      n: state.n - 1
    }))
  }
  shouldComponentUpdate(nextProps, nextState, nextContext) {
    return nextState.n !== this.state.n;
  }
  render() {
    console.log("render 了一次")
    return (
      <div>
        APP
        <div>
          {this.state.n}
          <button onClick={this.onClick}>+1</button>
        </div>
      </div>
    )
  }
}

问:shouldComponentUpdate有什么作用?

答:它允许我们手动判断是否要进行组件更新,我们可以根据应用场景灵活的设置返回值,以避免不必要的更新

render

用途:

  • 展示视图:return (<div>...</div>)
  • 只能有一个根元素
  • 如果有两个根元素,就要用<React.Fragment>包起来
  • <React.Fragment/>可以缩写成<></>

技巧:

  • render里面可以写if...else
class App extends React.PureComponent {
  constructor(props) {
    super(props)
    this.state = {
      n: 1
    }
  }
  onClick = () => {
    this.setState((state) => ({
      n: state.n + 1
    }))
  }
  render() {
    let message
    if (this.state.n % 2 === 0){
      message = <div>偶数</div>
    }else {
      message = <span>奇数</span>
    }
    return (
      <>
        {message}
        <button onClick={this.onClick}>+1</button>
      </>
    )
  }
}
  • render里面可以写?:表达式
class App extends React.PureComponent {
  constructor(props) {
    super(props)
    this.state = {
      n: 1
    }
  }
  onClick = () => {
    this.setState((state) => ({
      n: state.n + 1
    }))
  }
  render() {
    return (
      <>
        {this.state.n % 2 === 0 ?<div>偶数</div> : <span>奇数</span>}
        <button onClick={this.onClick}>+1</button>
      </>
    )
  }
}
  • render里面不能直接写for循环,需要用数组
class App extends React.PureComponent {
  constructor(props) {
    super(props)
    this.state = {
      n: 1,
      array: [1,2,3],
    }
  }
  onClick = () => {
    this.setState((state) => ({
      n: state.n + 1
    }))
  }
  render() {
    let result = []
    for (let i = 0; i<this.state.array.length; i++){
      result.push(this.state.array[i])
    }
    return result
  }
}
  • render里面可以写array.map(循环),记住所有的循环里面都要加key
class App extends React.PureComponent {
  constructor(props) {
    super(props)
    this.state = {
      n: 1,
      array: [1,2,3],
    }
  }
  onClick = () => {
    this.setState((state) => ({
      n: state.n + 1
    }))
  }
  render() {
    return this.state.array.map(n => <span key={n}>{n}</span>)
  }
}

conponentDidMount()

用途:

  • 在元素插入页面后执行代码,这些代码依赖DOM
  • 比如你想获取div的高度,就最好在这里
  • 此处可以发起加载的AJAX请求(官方推荐)
  • 首次渲染会执行此钩子

componentDidUpdate()

用途:

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

componentWillUnmount

用途:

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

举例:

  • 如果在componentDidMount里监听了window scroll,那么你就要在componentWillUnmount里取消监听
  • 如果你在componentDidMount里创建了Timer,那么你就要在componentWillUnmount里取消Timer
  • 如果你在componentDidMount里创建了AJAX请求,那么你就要在componentWillUnmout里取消请求