React Class组件详解

157 阅读6分钟

学习内容:

  • 英语小课堂
  • 创建方式
  • props和state
  • 生命周期

一、英语小课堂

  • derived 导出的、衍生的、派生的
  • super class 超类、父类
  • mount 挂载
  • render 渲染
  • property 属性
  • state 状态

二、创建方式

两种创建方式

1. ES5

import React from 'react'
const A = React.createClass({
    render(){
        return (<div>hi</div>)
    }
})
export default A

2. ES6

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

3.两种方法比较

ES5已经过时,基本不用,如果IE8的浏览器要用,webpack+Babel转换ES6为ES5

三、props外部数据

1.对外部数据的初始化

  • 要传出的外部数据被包装成为一个对象
  • {name:'yuyuan',onClick:……,childrean:'hi'}
  • 此处的onClick是一个回调
class Parent extends React.Component{
    constructor(props){
    super(props)
    this.state = {name:'yuyuan'}
    }
    onClick = ()=>{}
    render(){
        return (
            <B name={this.state.name  //从这里开始就是包装了
            onClick = {this.onClick}
            }>hi</B>
        )
    }
}

2.外部数据的引入和读取

  • 通过传参的形式引入,这样this.props就是外部数据对象的地址了
  • 通过this.props.xxx读取
class B extends React.Component{
    constructor(props){
        super(props) //引入
    }
    render(){
        return(
            <div onClick={this.props.onClick}>
            {this.props.name}
            {this.props.children}  //读取
            </div>
        )
    }
}

3.一些对props的SB写入操作

  • 改props的值(改一个地址) this.props={另一个对象}
  • 改props的属性 this.props.xxx = 'hi' 应该由传入原始数据的一方来修改数据,没有理由进行外部的更新

4.关于props的钩子——componentWillReceiveProps

作用:当组件接受新的props的时候,会触发此钩子,得到新旧的props值

但是这个钩子已经过气被弃用,学习仅仅是防止看不懂之前的代码

props钩子使用示范

import React from 'react'
import ReactDOM from 'react-dom'
class App extends React.Component{
    constructor(props){
        super(props)
        this.state = {n:0}
    }
    onClick = ()=>{this.setState({n:this.state.n+1})}
    render(){
        return (
            <div>n:{this.state.n} -App <button onClick={this.onClick}>+1</button>
                <B name={this.state.n} onClick = {this.onClick}/>
            </div>
        )
    }
}

class B extends React.Component{
    componentWillReceiveProps(newProps,nextContent){  //钩子
        console.log('旧的props',this.props)
        console.log('props变了')
        console.log('新的props',newProps)
    }
    constructor(props){
        super(props)
    }
    render(){
        return (
        <div>name:{this.props.name} - B <button onClick={this.props.onClick}>+1</button></div>
        )
    }
]

ReactDOM.render(<App />,document.getElementById('root'))

5.props的作用

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

四、state内部数据

1. state的初始化

    this.state = {user:{name:'yuyuan',age:18},n:0}

2. state的读取

    {this.state.n}
    {this.user.name}

3. state的写入

  • setState不会立刻改变this.setState,会在当前代码运行完后,再去更新this.state,从而触发UI更新
  • fn会在写入成功后执行 fn使用示例

A.对象式写入

对象式写入的案例

this.setState(newState,fn)

B.函数式写入

函数式写入的案例

this.setState((state,props)=>(newState),fn)

五、生命周期 Lifecycle

1.DOM的生命周期

  • div的create/constructor过程
let div = document.createElement('div')
  • div初始化state
div.textContent = 'hi'
  • div的mount过程
document.body.appendChild(div)
  • div的update过程
div.textContent = 'Hi'
  • div的unmount过程
div.remove()

2.React的生命周期

React生命周期列表

重要列表

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

不重要

  • static getDerivedStateFromProps()
  • getSnapshotBeforeUpdate()
  • static getDerivedStateFromError()
  • componentDidCatch()

constructor()

用途:

  • 用来初始化state
  • 用来初始化props
  • 用来写bind this
constructor(){
……
this.onClick.bind(this)
}
//上面的语法等价于新语法
onClick =()=>{}
constructor(){……}
  • 没有state要写的情况下,constructor可以不写

shouldComponentUpdate()

1.页面刷新原理解释

state ---render---> DOM ----> UI

graph TD
oldState.Vs.newState --> 比较两个state是否相同 --> 不同就render数据 --> 创建虚拟DOM --> diff比较两个虚拟DOM是否相同 --> 不同就刷新UI界面
2.state不同DOM相同的实例

因为React中对数据进行修改不是在原来的数据上进行的。即使值相同也会出现对象的地址不同导致比较时,React认为这两个数据就是不同的,但是此时render后创建的虚拟DOM是一样的,UI就不会刷新。

不同state相同DOM

import React from 'react'
import ReactDOM from 'react-dom'

class App extends React.Component{
    constructor(props){
        super(props)
        this.state = {n:0}
    }
    onClick = ()=>{
        this.setState(state=>({n:state.n+1}))
        this.setState(state=>({n:state.n-1}))
        }
    render(){
        console.log('render了!')
        return (
            <div>App<button onClick={this.onClick}>+1</button></div>
        )
    }
}

ReactDOM.render(<App/>,document.getElmentById('root'))
3.shouldComponentUpdate()手动选择是否更新组件

shouldComponentUpdate使用实例

在上一个案例的render前写上

shouldComponentUpdate(newProps,newState){
    if(newState.n === this.state.n){return false}else{ return true}
}

return false 表示不刷新组件,return true表示刷新组件

4.shouldComponentUpdate的替代PureComponent

写原来的阻止更新,只能自己手动一个一个去比较,太麻烦了,React内置了新的方法帮你去一个一个地判断。

PureComponent工作原理

对每一个state和props的key以及value进行比较,如果这些是相同的,就不会去更新组件,创建新的虚拟DOM了。

使用示例:

class App extends React.PureComponent{
    ……
}
5.阻止组件更新的过程

shouldComponentUpdate和PureComponent比较了state的key和value,相同就直接没有新的虚拟DOM的创建,不会刷新UI了。

6.面试题:shouldComponentUpdate有什么用?

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

render()

1. render()创建虚拟DOM元素用于展示视图

如何验证render()创建的是虚拟DOM呢? 查看render的是个什么东西

2.render中只能有一个根元素
render(){
    return (
    <div>hi</div>
    <div>Hi</div>
    )
}

这样会报错,因为有两个根元素

3.<><React.Fragment>占位根元素

用来解决一个根元素的问题,将<React.Fragment>或者</>作为占位的根元素

render(){
    return (
        <React.Fragment>
        <div>hi</div>
        <div>Hi</div>
        </React.Fragment>
    )
}

写这么多太长了,react给你简化

render(){
    return (
        <>
        <div>hi</div>
        <div>Hi</div>
        </>
    )
}
4.render()中的if-else

render()中可以直接写if-else,当然也可以用{}来写?:表达式

if-else示例

render(){
    let message = ''
    if(this.state.n%2===0){message=(<div>偶数</div>)}
    else{message=(<div>奇数</div>)}
    return (
    <div>{this.state.n}<button onClick={this.onClick}>+1</button>{message}</div>
    )
}

?;示范

render(){
    return (
        <div>{this.state.n}<button onClick={this.onClick}>+1</button>
        {this.state.n%2===0 ? <div>偶数</div> : <div>奇数</div>}
        </div>
    )
}
5.render()中的循环

因为循环没有返回值,所有要用一个数组接住循环的结果,或者使用map

借助数组

render(){
    let result =[]
    for(let i=0;i<this.state.arr.length;i++){
        result.push(this.state.arr[i])
    }
    return result
}

使用map

render(){
    return (
        <div>
            {this.state.arr.map(n=>(<div key={n}>{n}</div>)}
        </div>
    )
}

componentDidMount()

1.元素插入界面后执行,依赖于DOM

通过componentDidMount打印元素宽度

class App extends React.Component{
    constructor(props){
        super(props)
        this.state = {width:undefined}
    }
    componentDidMount(){
        const div = document.getElementById('xx')
        const {width} = div.getBoundingClientRect()
        //析构赋值 width= div.gBCR().width
        this.setState({width})
    }
    render(){
        return (
            <div id='xx'>halo {this.state.width}</div>
        )
    }
}
error:
  • 改写state的值直接写 this.state.width = width 应该写setState
  • setState对象法后直接console.log,大哥,你这个时候打印的肯定不是setState后的值啊
2.通过React.createRef()来标识div

div中的id很有可能会出现写着写着就重复的情况,所以用ref来写标识

  • ref的初始化 this.ref = React.createRef()
  • ref的读 <div ref={this.ref}>
  • ref的写 const div = this.ref.current
class App extends React.Component{
    ref = undefined //初始化
    constructor(){
        super()
        this.ref = React.createRef() //初始化
        this.state = {width:undefined}
    }
    componentDidMount(){
        const div = this.ref.current  //写
        const {width}  = div.getBoundingClientRect()
        this.setState({width}) 
    }
    render(){
        return ( //读取
            <div ref={this.ref}>halo {this.state.width}</div>
        )
    }
}
error:
  • ref = undefined 不要写在constructor里面,也不要用let定义
  • this.ref = React.createRef() 不要写在state里面,也不要忘记写this
3.与AJAX

此处可以发起加载数据的AJAX请求,官方推荐

4.钩子的执行

首次渲染会执行此钩子

componentDidUpdate()

看官方文档吧

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

componentWillUnmount

  • 用于组件将要被移除页面然后被销毁时执行
  • unmount过的组件不会再次被mount,你只有去再去创建一次
    • 谁污染谁治理
    • cDidMount监听了window scroll ,cWillUnmount就要取消监听
    • cDM创建了Timer,cWU就要取消Timer
    • cDM里面创建了AJAX请求,cWU里面就要取消请求

3.对钩子的总结

  • constructor 初始化state
  • render 创建虚拟DOM
  • shouldComponentUpdate 返回false更新组件,记得要写return true 不写的话,undefined也等同于false
  • componentDidMount 组件已经出现在界面
  • componentDidUpdate 组件已更新
  • componentWillUnmount 组件将死

分阶段看钩子的执行顺序

React钩子.png