React学习笔记 - class组件

375 阅读4分钟

一、两种方式创建 Class 组件

1、ES 5(过时)

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

ES 5 不支持 class,所以使用了这种方式。

2、ES 6

class Demo extends React.Component{
    constructor(porps){
        super(props)
    }
    render(){
        return <div>hi</div>
    }
}

二、props 属性/外部数据

读props

使用组件时传入外部数据:

<Demo name={x} onClick={fn}>hi</Demo>

其中的nameonClick就是外部数据,它们会被包装成对象:{name: x, onClick: fn, children: 'hi'},传入类组件constructorprops参数,并且成为this.props

写props

!!不准改写props!!

原则:外部数据应该由数据的主人进行更改

相关钩子

componentWillReceiveProps钩子

  • 当组件接收新的props时,触发此钩子
  • 已被弃用,更名为UNSAFE_componentWillReceiveProps不要使用

props的作用

  • 接收外部数据
    • 只能读不能写
    • 外部数据由父组件传入
  • 接收外部函数
    • 在合适的时机调用函数
    • 函数一般是父组件的函数

三、state & setState 内部数据

改写statesetState函数

steState的用法

setState可接收两个参数,即this.setState(???, fn)

(1)第一个参数

第一个参数有两种写法:

  1. 传入全新state
this.setState({x: this.state.x + 1})

必须要传入一个全新的对象,不要改动旧的state

``setState会自动将旧state`的第一级属性合并

  1. 传入一个回调函数
this.setState(state => {return {x: state.x + 1}})

setState会调用函数,传入旧的state,将返回值作为新的state

【注】:

setState是异步的,它不会立刻更新state,在当前代码运行完毕后,才会更新state,然后触发UI更新。基于此,推荐使用第二种调用方法。

(2)第二个参数

第二个参数是一个函数,在state成功更新后会被调用,很少使用。

四、常用生命周期

1、constructor

创建组件时执行

用途:

  • 初始化propsstate

  • 用于 bind this:

constructor(){
    // ...
    this.onClick = this.onClick.bind(this)
}
  • 可不写(初始化state必须写)

2、shouldComponentUpdate

在数据更新时执行

2-1 用法

  • 返回true,不阻止UI更新
  • 返回false,阻止UI更新

2-2 用途

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

2-3 案例

state{n:1}变为{n:1}(新的对象),虽然两个对象看似一样,但它们是不同的对象,React会启动更新UI的流程,执行render(),经对比发现两个虚拟DOM没有不同,于是不更新视图,但render却实实在在执行了,此时可以用钩子函数阻止启动更新UI的流程。

【例】

shouldComponentUpdate(newProps, newState){ //调用时会依次传入新props、新state
    if(newState.n === this.state.n){
        return false
    }else{
        return true
    }
}

2-4 改进:React.PureComponent

React内置了自动进行上述比较的功能,用React.PureComponent代替React.Component即可

class App extends React.PureComponent{
    //...
}

stateprops更新时,会自动对新旧stateprops的第一级属性进行比较,若完全一样,则不触发UI更新。只要有任何一个属性不同,就会触发。

3、render

生成虚拟DOM

3-1 用法

render的返回值有要求:

  • 只能有一个根元素
  • 若有多个根元素,要用<React.Fragment>包围,可以简写为<>
<React.Fragment>
    <div>元素一</div>
    <div>元素二</div>
</React.Fragment>

//等价于:
<>
    <div>元素一</div>
    <div>元素二</div>
</>

3-2 技巧

render中可以使用各种语句结构

if...else

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

?:表达式

render(){
	return (
    	<>
        	{this.state.n % 2 === 0? <div>偶数</div>: <span>奇数</span>}
        	<button>+1</button>
        </>
    )
}

循环结构

render中使用循环,需借助数组:

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

最常用的是借助数组的map方法:

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

【注】:每次循环得到的结果必须要有key 属性

4、componentDidMount

在元素渲染到页面后执行,通常是一些依赖于DOM的操作,只在首次渲染后执行

4-1 用途

(1)【例】:获取页面中div的宽度

【方法一】通过id获取div

componentDidMount(){
    const div = document.getElementById('xxx')
    const {width} = div.getBoundingClientRect()
    this.setState({width})
}

当div渲染到页面中时,执行函数,获取宽度值放在this.state.width

【方法二】通过React虚拟DOM的ref属性获取div

constructor(){
    //...
    this.divRef = React.createRef()
}
componentDidMount(){
    const div = this.divRef.current
    const {width} = div.getBoundingClientRect()
    this.setState({width})
}
render(){
    return <div ref={this.divRef}>Hello</div>
}

(2)在此处发起加载数据的AJAX请求(官方推荐)

5、componentDidUpdate

在视图更新后执行,首次渲染不会执行

用途:

  • 此处也可以发起AJAX请求,但用于更新数据。例如,用户ID改变,重新请求新ID的信息。

  • 在此处setState会引起无限循环,要使用if进行条件判断

  • shouldComponentUpdate返回false,则此钩子不触发

传参:

依次为prevPropsprevStatesnapshot(暂时忽略)

6、componentWillUnmount

组件将要被移出页面并被销毁时执行

用途:

销毁在mount时创建的一些内容,释放内存,例如:

  • Mount里监听了window scroll,就要在unmount里取消监听
  • Mount里创建了Timer,就要在unmount里取消Timer
  • Mount里创建了AJAX请求,就要在unmount里取消请求

(实际上,可以不用理会这些)

7、生命周期总结

  • 首次渲染:

constructor ----> render ----> 更新UI ----> componentDidMount

  • 再次渲染:

props改变/setState()/forceUpdate() ----> shouldComponentUpdate ----> render ----> 更新UI ----> componentDidUpdate

  • 销毁:

componentWillUnmount