Class组件

76 阅读4分钟

创建方式

class A extends React.Component {
    constructor(props){
        super(props);
    }
    render() {
        return (
            <div>hi</div>
        )
    }
}
export default A;

props外部数据

  1. 外部数据被包装成一个对象,B 组件接收到的 props 的值是:{name:'frank, onClick:..., children:'hi'}【其中onClick是回调函数】

代码示例:

class Parent eztends React.Component{
    constructor(props){    // 初始化,没有第4行的话2-5行可省略不写
        super(props);
        this.state = {name:'frank'}
    }
    onClick = ()=>{}
    render(){
        return <B name={this.state.name} onClick={this.onClick}>hi</B>
    }
}
  1. this.props.xxx读取
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>      
    }
}
  1. props只读不写 因为props保存的是值的地址。props是外部数据,就应该由外部数据更新(数据的主人对数据进行更改)

state内部数据

  1. 初始化state
class B extends React.Componrnt{
    construcyor(props){
        super(props);
        this.state = {
            user: {name:'frank', age:18}
        }
    }
    render(){}
}
  1. 读写state 读用this.state.xxx

写用this.setState(newState, fn) 【fn是回调函数】

setState后不会立马改变this.state,会在当前代码运行完后再去更新this.state,从而触发更新UI,推荐函数的写法this.setState((state,props) => newState,fn)

bug示例:

  1. onClick直接修改对象,导致点击+1按钮以后x只加了一次
  2. onClick2写成函数,点击一次+1按钮让x成功的一次性加了两个1

2写成对象函数,没有1异步更新·只改了一次的bug.png

  1. onClick写成回调嵌套,点击一次+1按钮让x逐步加了两个1

1写成回调嵌套,一次加1逐步变2.png

setState 的 shallow merge:
React 只会检查新 state 和旧 state 第一层的区别,并把新 state 缺少的数据从旧 state 里拷贝过来

生命周期(函数)

函数列表:

constructor() // 初始化state
static getDerivedStateFromProps()
shouldComponentUpdate() // return false阻止更新
render() // 创建虚拟DOM
getSnapshotBeforeUpdate()
componentDidMount // 组件已经吹出现在页面
componentDidUpdate() // 组件已更新
componentWillUnmount() // 组件将死
staticgetDerivedStateFromError()
componentDidcatch()

construtor

用途:

  1. 初始化props
  2. 初始化state,但此时不调用setState
  3. 用来写bind this
constructor(){
    this.onClick = this.onClick.bind(this)
}

// 新语法代替
onClick = ()=> {}
constructor(){}

shouldComponentUpdate

用途:

  1. 返回true表示不阻止UI更新
  2. 返回false表示阻止UI更新

shouldComponentUpdate作用.png

React内置了对比更新的函数React.PureComponent来代替React.Component,但它只会对比最外边的一层(浅对比)来决定是否render。

PureComponent 会在 render 之前对比新 state 和旧 state 的每一个 key,以及新 props 和旧 props 的每一个 key。
如果所有 key 的值全都一样,就不会 render;如果有任何一个 key 的值不同,就会 render。

质疑:为什么要用新对象而不再this.state身上直接改?因为这是React的编程哲学

uTools_1688265620257.png

示例代码:

class App extends React.Component{  // 可换为React.PureComponent代替should生命周期函数
    constructor(props){
        super(props);
        this.state = {
            n: 1
        };
    }
    onClick = ()=> {
        this.setState(state => ({n: state.n + 1}));
        this.setState(state => ({n: state.n - 1}));
    }
    shouldComponentUpdate(newProps,newState){
        if(newState.n === this.state.n){
            return false
        }else{
            return true
        }
    }
    render(){
        console.log('render了一次')    // 只render了一次
        return (
            <div>
                {this.state.n}
                <button onClick={this.onClick}>+1-1</button>
            </div>
        )
}

render

用途:

  1. 展示视图,return一个虚拟DOM(就是一个对象)
  2. 只能有一个根元素
  3. 如果有两个根元素,就要在外层再加一个div或者用Reac.Fragment包起【可缩写成<></>】。两者的区别是:后者只是做一个占位符,不会真正出现

uTools_1688266147046.png

注意:

  1. render里可写if else
  2. render里可写? :表达式
  3. render里不能写for循环,需要用数组(否则只渲染第一个值)
  4. render里可写array.map循环【记得加key!】(曾经的疑问:为什么不直接输出创建的数组?当然可以了,但那样不就无法进行其他操作了吗,而且react推荐我们用创建新对象)

三目运算符 uTools_1688266535753.png

for循环以后遍历push入新数组 uTools_1688266715038.png

map遍历数组并塞入标签(加key,否则warning) uTools_1688267617145.png

uTools_1688267836511.png

componentDidMount(挂载后执行的钩子)

用途:

  1. 在元素插入页面后执行的代码,这些代码依赖DOM【获取div高度】
  2. 发起加载数据的AJAX请求(官方推荐把请求放在此处钩子)
  3. 首次渲染执行此钩子

获取div高度(不能写在constructor里,因为此时页面未)还可以使用ref,后面讲 uTools_1688268528764.png

componentDidUpdate

用途:

  1. 在视图更新后执行此代码
  2. 发起AJAX请求,用于更新数据
  3. 首次渲染不会执行此钩子
  4. 此处setState可能会引起无限循环(互相调用),除非放在if里
  5. 若shouldComponentUpdate返回false,则不会触发此钩子

参数:看文档

componentWillUnmount

用途:

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

举例:(防止占内存,其实可不写)

  • 如果你在c..Didmount里监听了window scroll,那么你就要在c..DidUnmount里取消监听
  • 如果你在c..Didmount里监听了Timer,那么你就要在c..DidUnmount里取消Timer
  • 如果你在c..Didmount里监听了AJAX,那么你就要在c..DidUnmount里取消请求

分阶段看钩子执行顺序:

首次渲染:创建-->渲染-->挂载

再次渲染:内/外部数据变化,forceUpdate(看文档)-->触发Should...Update-->往下进行流程判断

销毁:可以不做这件事 uTools_1688265343940.png

React弃用的生命周期钩子

1、componentWillMount
2、componentWillUpdate
3、componentWillReceiveProps