React Class组件

简介: React Class组件

React Class组件


网络异常,图片无法展示
|

一.class组件的创建方式

创建 class 组件

两种方式

//第1种.ES5方式(过时)
import React from 'react'
const A = React.createClass({
  render() {
    return ( <div>hi</div> )
  }
})
export default A
// 由于ES5不支持class,才会有这种方式
//第2种.ES6方式(推荐)
import React from 'react';
class B extends React.Component {
  constructor(props) {
    super(props);
  }
  render() {
    return (
      <div>hi</div>
    )
  }
}
export default B;

浏览器不支持ES6怎么办?

用webpack+babel将ES6翻译成ES5即可

Props外部数据

对组件来说组件内就是内部,组件外就是外部\

1.传入props给B组件

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

2.初始化

class B extends React.Component {
    constructor(props) {
        super(props);
    }
    render(){}
}
//要么不初始化,即不写constructor
//要么初始化,且必须写全套(不写super直接报错)

效果

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

3.读取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读取

4.怎么写props?

永远不准对props进行任何的改写,不准写props!

//不要这样写
this.props={ 另一个对象}
this.props.xxx='hi'

相关钩子

ComponentWillReceiveProps钩子(被弃用)

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

面试题

Props的作用

1.接受外部数据

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

2.接收外部函数

在恰当的时机,调用该函数。比如说当某个div被点击时调用

该函数一般是父组件的函数

State(读) & setState(写)

1.初始化

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

2.读写State

读用this.state

this.state.xxx.yyy.zzz

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

第1个参数有2种情况:

1' this.setState(newState,fn)

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

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

这种方式的state反而更易于理解

fn会在写入成功后执行

写时会shallow merge

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

二、React有如下生命周期:

1.constructor() 构造,在这里初始化state 
2.shouldComponentUpdate() 是否应该更新,return false阻止更新 
3.render() 渲染,创建虚拟DOM
4.componentDidMount() 组件已挂载(已出现在页面)
5.componentDidUpdate() 组件已更新,当组件更新之后就会执行该函数
6.componentWillUnmount() 当组件将要卸载/死亡时就会执行该函数

一.constructor

用途

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

二.shouldComponentUpdate

是否应该更新,return false阻止更新

PureComponent是shouldComponentUpdate的优化,推荐用PureComponent

用途

返回true表示不阻止UI更新

返回false表示阻止UI更新\

面试题

请问 shouldComponentUpdate 有什么用?

这个钩子/生命周期允许我们手动判断是否要进行组件更新,我们可以根据应用场景灵活的设置返回值,以避免不必要的更新

示例:+1``-1数值不变,render仍旧会执行

import React from "react"
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(newProps, newState) {
    if (newState.n === this.state.n) {
      return false
    } else {
      return true
    }
  }
  render() {
    console.log('render了一次')
    return (
      <div>App
        <div>
          {this.state.n}
          <button onClick={this.onClick}>+1</button>
        </div>
      </div>
    )
  }
}
export default App

补充:JS中{}有个bug,如果你要返回一个对象,最好用()扩起来。

Render是每次都执行了,重复执行。有没有办法阻止render不必要的重复执行?用shouldComponentUpdateReact.shouldComponentUpdate

newProps就算用不到也不能删,占位的,删掉的话newState就变成第1个参数了

shouldComponentUpdate(newProps, newState){
  if (newState.n === this.state.n) {
    return false
  } else {
    return true
  }
}
可以这样写
shouldComponentUpdate(){ //arguments的用法
  if (arguments[1].n === this.state.n) {
    return false
  } else {
    return true
  }
}

PureComponent的用法(推荐)

PureComponent是shouldComponentUpdate的优化

用法:只需要修改继承React.PureComponent

class App extends React.PureComponent{ ... }

React提供了PureComponent给你优化,可代替shouldComponentUpdate。 PureComponent 会在 render 之前对比新旧state的每一个key,以及新旧props的每一个key。如果所有key的值全都一样就不会render,如果有任何一个key的值不同,就会 render。

三.生命周期之 render

渲染,创建虚拟DOM

网络异常,图片无法展示
|

用途详解

功能1.return一个虚拟DOM

表示DOM元素的对象旧就叫做虚拟DOM,return返回的<div>是一个虚拟DOM元素的对象。虚拟DOM就是个对象。

虚拟DOM长啥样?

render() {
  const x = (
    <div>App
      <div>
        {this.state.n}
        <button onClick={this.onClick}>+1</button>
       </div>
     </div>
   )
   console.log(x)
   return x
}

网络异常,图片无法展示
|

功能2.只能有一个根元素

如果有2个元素就要用<React.Fragment>包起来。

<React.Fragment></React.Fragment>可以简写为<></>

render() {
  return (
    <>
      <div>div1</div>
      <div>div2
        <div>
          {this.state.n}
          <button onClick={this.onClick}>+1</button>
        </div>
      </div>
    </>
 )}

好处:只是做占位的,渲染的时候并不存在。

网络异常,图片无法展示
|

技巧详解

1.render里面可以写if...else?:表达式

可以写if...else...?:表达式(推荐)、&&

render() {
  let message
  if (this.state.n % 2 === 0) {
    message = <div>偶数</div>
  } else {
    message = <div>奇数</div>
  }
  return (
    <>
      {message}
      <button onClick={this.onClick}>+1</button>
    </>
  )
}
//也可以直接这样
render() {
  return (
    <>
      {this.state.n % 2 === 0 ? <div>偶数</div> :<div>奇数</div>}
    //{this.state.n % 2 === 0 && <div>偶数</div>}
      <button onClick={this.onClick}>+1</button>
    </>
  )
}

2.render中循环的写法

Render里面不能直接写for循环,需要用数组,因为for循环没有返回值,所以只会循环一次。

一般会用map实现循环。

循环方法:1' 用变量 2' 用map

1' 用变量
render() {
  let result = []
  for (let i = 0; i < this.state.array.length; i++) {
    result.push(this.state.array[i])
  }
  return result
}
2' 用array.map
render() {//给每个元素包个span
  return this.state.array.map(n => <span key={n}> {n} </span>)
}

网络异常,图片无法展示
|

⚠️注意:所有的循环都需要绑定key

四.生命周期之 componentDidMount

当组件已挂载(已出现在页面)就会执行这个函数

网络异常,图片无法展示
|

获取当前div的宽度

1' 用id

import React from "react"
class App extends React.PureComponent {
    constructor(props) {
        super(props)
        this.state = {
            n: 1,
            width: undefined
        }
    }
    onClick = () => {
        this.setState(state => ({
            n: state.n + 1
        }))
    }
    componentDidMount() {
        const div = document.getElementById("xxx")
        const { width } = div.getBoundingClientRect()//析构写法
        this.setState({ width })
    }
    render() {
        return (
            <div id="xxx">当前div宽度是:{this.state.width}</div>
        )
    }
}
export default App

网络异常,图片无法展示
|

在元素插入页面后执行代码,这些代码依赖DOM。如果你想获取div的高度,就最好在componentDidMount里写。

2' 除了用id还可以用Ref

React提供了一种更方便的方式来获取div

React ref

class App extends React.PureComponent {
    divRef = undefined //先声明,告诉后来的程序员说,我会动态创建divRef的东西。直接写不利于阅读。
    constructor(props) {
        super(props)
        this.state = {
            n: 1,
            width: undefined
        }
        const divRef = React.createRef //第1步.创建引用
    }
    onClick = () => {
        this.setState(state => ({
            n: state.n + 1
        }))
    }
    componentDidMount() {//第3步.使用this.divRef获取div
        const div = this.divRef.current
        const { width } = div.getBoundingClientRect()
        this.setState({ width })
    }
    render() {
        return (//第2步.将创建的引用放到div上
            <div ref="{this.divRef}">当前div宽度是:{this.state.width}</div>
        )
    }
}

好处:不用担心div会冲突,因为用的是内部属性。

ComponentDidMount还可以发起"加载数据的AJAX请求",官方推荐写在这里。比如我要获取当前用户信息,既可以在constructor里写,也可以在componentDidMount里写,但是官方推荐写到componentDidMount

首次渲染一定会执行此钩子。

ComponentDidMount(){}没有参数,要获取什么东西只能通过this取。

五.生命周期之 componentDidUpdate()

当组件更新之后就会执行该函数,首次渲染不会执行此钩子。

网络异常,图片无法展示
|

1.在视图更新后执行代码。首次渲染不会执行此钩子,因为没有更新任何东西。

2.componentDidUpdate()可以发起AJAX请求,用于更新数据经验:  很少在componentDidUpdate发请求,一般都是在componentDidMount里。

3.如果在这个钩子里面再去setState可能会引起无限循环。 因为它们两个会互相调用,除非做一些条件判断。比如说,n是奇数时才会调用componentDidUpdate

4.如果阻止更新UI,componentDidUpdate返回false,则不触发此钩子。

5.参数

componentDidUpdate(prevProps, prevState, snapshot)

六.生命周期之 componentWillUnmount()

网络异常,图片无法展示
|

当组件将要卸载/死亡时就会执行该函数

1.组件从页面中移除并且从内存里干掉,才会触发并执行componentWillUnmount 移除页面就是从页面跑到内存里面。销毁就是从内存里干掉它。

2.unmount过的组件不能再吃unmount

举例

1.如果你在挂载时监听了window scroll事件,那你应该在组件要死时取消监听,因为如果你不取消那你监听有什么用呢。

2.如果你在挂载时创建了计时器Timer,那么组件要死时就要删掉。

3.如果你在挂载时创建了AJAX请求,那么你就要在componentWillUnmount里取消请求。

所有你发起的那些会长期有效的东西,在你死后你都得取消。但实际上绝大部分的前端都不会做上面的3件事情。因为浪费的只是用户的内存,这就是前端页面越来越占内存的原因,大家都只用内存不清除内存。

总结:  componentWillUnmount钩子是用来消除你之前做的一些可能会产生后果的一些事情,比如scroll、Timer、AJAX。

原则:  谁污染谁治理。

分阶段看钩子执行顺序:

网络异常,图片无法展示
|
能用函数组件就不用class组件,能用 React.PureComponent就不用 React.Component

相关文章
|
4月前
|
资源调度 前端开发 JavaScript
React 的antd-mobile 组件库,嵌套路由
React 的antd-mobile 组件库,嵌套路由
43 0
|
3月前
|
存储 前端开发 中间件
React组件间的通信
React组件间的通信
18 1
|
3月前
|
前端开发 应用服务中间件 数据库
react服务端组件
react服务端组件
21 0
|
3月前
|
前端开发 JavaScript
快速上手React:从概述到组件与事件处理
快速上手React:从概述到组件与事件处理
|
4月前
|
前端开发 JavaScript API
React组件生命周期
React组件生命周期
76 1
|
4月前
|
资源调度 前端开发 JavaScript
React组件
React组件
42 0
|
4月前
|
存储 前端开发 JavaScript
探索 React Hooks 的世界:如何构建出色的组件(下)
探索 React Hooks 的世界:如何构建出色的组件(下)
探索 React Hooks 的世界:如何构建出色的组件(下)
|
4月前
|
缓存 前端开发 API
探索 React Hooks 的世界:如何构建出色的组件(上)
探索 React Hooks 的世界:如何构建出色的组件(上)
探索 React Hooks 的世界:如何构建出色的组件(上)
|
4月前
|
存储 前端开发 JavaScript
React组件中如何通讯
React组件中如何通讯
16 0
|
4月前
|
前端开发
react 使用 antd-mobile组件库实现下滑加载数据
react 使用 antd-mobile组件库实现下滑加载数据