react

✍️ Tangxt ⏳ 2021-03-21 🏷️ 类组件

Class 组件详解

★英语小课堂

英语小课堂

★两种创建 Class 组件的方式

1)ES5 方式(过时)

ES5

2)ES6 方式

ES6

3)哪种好

哪种好

★Props(外部数据)

1)props

外部数据一般都是来自父元素的内部数据:

外部数据

初始化:

初始化

2)子组件读取 props

读取

3)谁写 props

写 props

props主人对数据进行更改:

写 props

4)相关钩子

componentWillReceiveProps

黑话:帮会、盗匪、流氓团伙等所使用的只有同伙才能听懂的暗语 -> 钩子,只有程序员才能听懂的暗语!

import "./styles.css";
import React from "react";

export default class App extends React.Component {
  constructor() {
    super();
    this.state = {
      n: 0
    };
  }
  onClick = () => {
    this.setState({
      n: this.state.n + 1
    });
  };
  render() {
    return (
      <div className="App">
        App <button onClick={this.onClick}>+1</button>
        <B name={this.state.n}>
          <h1>1</h1>
          <h2>2</h2>
        </B>
      </div>
    );
  }
}

class B extends React.Component {
  // constructor(props) {
  //   super(props);
  //   console.log(props);
  // }
  componentWillReceiveProps(newProps, nextContext) {
    console.log("旧的 props");
    console.log(this.props);
    console.log("props 变化了");
    console.log("新的 props");
    console.log(newProps);
  }
  render() {
    return (
      <div>
        {this.props.name}
        <div>{this.props.children}</div>
      </div>
    );
  }
} //通过 this.props.xxx 读取

componentWillReceiveProps

该钩子的作用就是「通知你 props 什么时候变化」

注意,componentWillReceiveProps钩子已被弃用 -> 请不要使用!

看懂旧代码

💡:为什么要么不写constructor,要么写全constructor?我没有写constructor,而我在 JSX 是可以直接使用this.props拿到外部对象的?

如果你不写 constructor,那么 React 内部,会在组件实例化后,执行这行代码:

// React 内部
const instance = new YourComponent(props);
instance.props = props;

➹:译-我们为什么要写 super(props)?

5)props 的 作用

作用

★State(内部数据)

state

1)初始化 State

init State

2)读写 State

读写 State

不守规矩的写 State:

不守规矩

3)setState 第一个参数用对象和函数的区别

需求:两次 setState

两次 setState

this.state.x在读的时候,还是旧的x

旧值

之前说过了,函数可以延迟求值,而这个 this.state.x 是先确定了值再给setState的! -> 第一个setState执行后,x并咩有变化,还是原来的1,当执行到第二个setStatethis.state.x的值依旧是1

所以,如何让两次setState的结果是两次+1呢?

三种姿势:

  1. 一次计算好了再写setState -> 这样就只写一次setState
  2. 使用setTimeout()=>{ two setState } -> 在 callback 里边写两次 setState
  3. 用函数 -> 满足你写两次setState的需求

用定时器:

定时器

这种姿势的setState看上去就像同步了! -> 如果不加setTimeout就是异步了! -> 总之,setState本质还是异步的!

用函数参数:

用函数

其实还可以用setState的第二个参数来搞(少用):

使用 callback

💡:为什么要多次setState

因为这个 Web 应用程序变复杂了,所以多次 setState 就可能出现了!

然而一个setState就意味着一次render?即 DOM 视图会立刻发生变化吗? -> 不 -> 这是「batches updates」,也就是先把你要更新的说清楚,再一次性去render -> 类似于,你要买 10 个苹果,但你却一个袋子装一个这样去结账,结账了 10 次! -> 为何不 10 个装在一起,再一次性结账呢?

为什么要用函数参数? -> 我们需要用到上一次setState的结果!

类比「王牌对王牌:传声筒传话游戏」:

setState

💡:为什么用了函数参数后,第二个setStatestate值就是上一个setState更新后的值?

➹:译 - 在 setState 中使用函数替代对象

💡:setState的源码?

ReactComponent.prototype.setState = function(partialState, callback) {
  this.updater.enqueueSetState(this, partialState);
  if (callback) {
    this.updater.enqueueCallback(this, callback, 'setState');
  }
};

partialState -> 部分state -> 表示「不影响其它的state,只更新我要更新的」

➹:React - setState 源码分析(小白可读)

★生命周期

1)概述

原生 JS 里边的生命周期:

生命周期

React 里边的生命周期:

React

我们必须要会的钩子:

必须要会的钩子

componentWillUnmount -> 组件将要消失!

2)constructor

constructor

3)shouldComponentUpdate

shouldComponentUpdate

什么时候触发这个钩子? -> state变了就触发

用法

如果我们没有用shouldComponentUpdate,即便n的值还是原来的1,也会执行render方法,因为state已经变了,state这个对象换了,那么 React 就得更新数据,当然,这并不会更新 UI:

不更新 UI

总之,你不写shouldComponentUpdate去处理的话,render是多执行了的,而我们用这个生命周期钩子是可以阻止它执行的!

如何阻止? -> 判断新的旧的相等就不执行

提高性能

示例代码:

示例代码

💡:pureComponent?

它会在 render 之前对新旧 state 进行对比(浅对比,只对比一层

pureComponent

如果你多此一举的写了shouldComponentUpdate,那么控制台就会警告你:

不用写

面对这种情况:

这种情况

pureComponent也就不好使了!即name依旧是frank,还是会调用render,当然,这 UI 并不会更新!

总之:

总结

4)render

它给 React 使用者的印象:

render

1、虚拟 DOM

vDOM

2、React.Frangment

Frangment

3、if else

if else

可以简写成三目运算符:

三目运算符

如果你想只展示一种,那么你可以简写成&&

&&

4、循环

如果你用for循环,你这样写:

class() {
  render() {
    for (let index = 0; index < this.state.arr.length; index++) {
      const element = this.state.arr[index];
      return element
    }
  }
}

循环一遍就结束,因为return了呀!

返回的是数组,而不是一个数组元素:

数组

Warning: Each child in a list should have a unique "key" prop -> 别忘了,要为每个 react 元素加一个唯一的key属性

使用map更高级),而不是for

map

5)componentDidMount

组件已经挂载了!

componentDidMount

需求:拿到一个元素的width,并把它显示页面!

组件被挂载之后才能拿到:

width

如果你在创建的时候拿这个div元素,那它就是个null了:

拿不到

总之,我们可以在componentDidMount这个钩子里边发起 Ajax 请求,当然,也可以在constructor里边发,之所以选择在componentDidMount里边发请求,实属偏好! -> 如果面试问「在哪儿发 Ajax 请求?」,你就会回到说在componentDidMount里边发,因为官方推荐哈!

注意:这个钩子没有参数

💡:使用 ref 姿势获取节点?

ref

6)componentDidUpdate

更新 UI 后执行

componentDidUpdate

注意点:

关于第三个参数:如果组件实现了 getSnapshotBeforeUpdate() 生命周期(不常用),则它的返回值将作为 componentDidUpdate() 的第三个参数 “snapshot” 参数传递。否则此参数将为 undefined

参数测试

💡:面试官问:请问我要发起一个 Ajax 请求,在哪里可以发起比较好?

componentDidMount() 里面发起比较好

面试官再问:请问还有没有其它钩子可以加载数据(发起 Ajax)?

如果有必要的话在 componentDidUpdate() 里面也可以发起请求,只不过请求是用来更新数据的,所以这个钩子显然不能这样做,因为这样很没有意义!

7)componentWillUnmount

将死之人(页面中移除,内存也要清除)

componentWillUnmount

8)钩子的执行顺序

钩子的执行顺序

★总结

什么周期方法

函数列表:

函数列表

正确的认识:

组件的state变了,就是指组件实例的state属性变了(遵守不可变数据理念),而不是说state旗下的那些属性变了!