React学习5(React class 组件)

类组件-无状态组件和有状态组件

  • 1.无状态组件

    • 组件本身不定义状态,没有组件生命周期,只负责 UI 渲染。
    • React16.8之前的函数组件都是无状态组件,Hooks 出现后函数组件也可以有状态。
  • 2.有状态组件

    • 组件本身有独立数据,拥有组件生命周期,存在交互行为。
    • class 组件可以定义组件自己的状态,拥有组件生命周期,它是有状态组件。
    1. 它们的区别
      无状态组件由于没有维护状态只做渲染,性能较好。有状态组件提供数据和生命周期,能力更强。
  • 4.如何去选择

    • React16.8之前,组件不需要维护数据只渲染就使用函数组件,有数据和交互使用类组件
    • React16.8之后,Hooks出现给函数提供状态,建议使用函数组件即可
Rreact State(状态)

React 把组件看成是一个状态机(State Machines)。通过与用户的交互,实现不同状态,然后渲染 UI,让用户界面和数据保持一致。
React 中,class组件有自己的state来维护组件内部的数据, 只需更新class组件的 state,然后就会根据新的 state 重新渲染用户界面(不要操作 DOM)。state是私有的,完全受控于当前组件.

定义state属性定义组件状态,属于组件自己的数据,它的值是个对象
使用state的时候通过this去访问即可,例如:this.state.xxx
数据发生变化,驱动视图更新。组件初始在构造方法内可以给state赋值,然后在其他地方只能通过this.setState()来修改state的数据,修改数据之后会重新执行render方法进行刷新渲染视图

以下实例创建一个名称扩展为 React.Component 的 ES6 类,在 render() 方法中使用 this.state 来修改当前的时间。
添加一个类构造函数来初始化状态 this.state,类组件应始终使用 props 调用基础构造函数。

import React, {Component} from 'react';

class StateDemo extends Component {
    constructor() {
        super();
        this.state = {
            name: 'react',
            now: new Date()
        };
    }
    render() {
        return (
            <div>
                <h1>hello {this.state.name}</h1>
                <div>当前时间是: {this.state.now.toLocaleString()}</div>
                <button onClick={()=>{this.setState({now: new Date()})}} >刷新时间</button>
            </div>
        );
    }
}

React Props

state 和 props 主要的区别在于 props 是不可变的,而 state 可以根据与用户交互来改变。这就是为什么有些容器组件需要定义 state 来更新和修改数据。 而子组件只能通过 props 来传递数据。

Props的只读性

组件无论是使用函数声明还是通过 class 声明,都绝不能修改自身的 props。

React 非常灵活,但它也有一个严格的规则:
所有 React 组件都必须像纯函数一样保护它们的 props 不被更改。

默认 Props

可以通过组件类的 defaultProps 属性为 props 设置默认值,实例如下:

class Welcome extends React.Component {
  render() {
    return (
      <h1>Hello, {this.props.name}</h1>
    );
  }
}
Welcome.defaultProps = {
  name: 'react'
};
 
const element = <Welcome/>;
 
ReactDOM.render(
  element,
  document.getElementById('root')
);

Props 验证

React.PropTypes 在 React v15.5 版本后已经移到了 prop-types 库,使用验证需要引入对应的cdn库

<script src="https://cdn.bootcss.com/prop-types/15.6.1/prop-types.js"></script>

rops 验证使用 propTypes,它可以保证我们的应用组件被正确使用,React.PropTypes 提供很多验证器 (validator) 来验证传入数据是否有效。当向 props 传入无效数据时,JavaScript 控制台会抛出警告。

class Welcome extends React.Component {
  render() {
    return (
      <h1>Hello, {this.props.name}</h1>
    );
  }
}
Welcome.propTypes = {
  name: PropTypes.string
};
 
const element = <Welcome name={123} />;
 
ReactDOM.render(
  element,
  document.getElementById('root')
);

类组件-绑定事件
掌握类组件中绑定事件的方式,和获取事件对象的方式。

大致步骤:

在类中声明事件处理函数,在标签上使用on+事件名称={处理函数}的方式绑定事件,事件名称需要遵循大驼峰规则。
处理函数默认的参数就是事件对象,可以使用事件对象处理默认行为和事件冒泡。

class组件的创建

react的组件可以定义为class或函数的形式,class组件目前提供了更多的功能. 如需定义class组件,需要继承React.Component

class Welcome extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}

React.Component 的子类中有个必须定义的 render() 函数。
强烈建议不要创建自己的组件基类。 在 React 组件中,代码重用的主要方式是组合而不是继承.
React 并不会强制你使用 ES6 的 class 语法。如果你倾向于不使用它,你可以使用 create-react-class 模块或类似的自定义抽象来代替。请查阅不使用 ES6 了解更多

组件的生命周期

每个组件都包含“生命周期方法”,你可以重写这些方法,以便于在运行过程中特定的阶段执行这些方法。在下述列表中,常用的生命周期方法会被加粗。(生命周期图谱)

在这里插入图片描述

函数的生命周期 主要分为三个阶段 :挂载(已插入真实 DOM)、更新(正在被重新渲染)、卸载(已移出真实DOM) .

class LifeDemo extends Component {
    constructor() {
        super();
        console.log('======执行了构造方法');
        // 构造方法中直接给state赋初始值 不能执行setState
        this.state = {name:'react'}
    }

    componentWillMount() {
        console.log('=======组件将要挂载,执行了componentWillMount')
    }

    componentDidMount() {
        console.log('======组件挂载到dom节点完成,执行了componentDidMount方法');
    }

    shouldComponentUpdate(nextProps, nextState, nextContext) {
        console.log('=====执行了shouldComponentUpdate 组件在受到props或者state更新要判断是否执行render方法刷新视图, 返回false则不执行render');
        // 当state中name变为vue时,视图不更新
        if (nextState.name === 'vue') {
            return false;
        }
        return true;
    }

    componentDidUpdate(prevProps, prevState, snapshot) {
        console.log('=====组件更新完成之后,执行了componentDidUpdate')
    }

    componentWillUnmount() {
        // 一般用于处理消除定时器、订阅
        console.log('======组件将要移除时,执行componentWillUnmount')
    }


    render() {
        console.log('======执行了render方法');
        return (
            <div>
                组件生命周期Demo
                <div>hello {this.state.name}</div>
                <input type="text" value={this.state.name}
                       onChange={(e)=>{
                           console.log(e.target.value);
                           console.log(this);
                           this.setState({name: e.target.value})
                       }} />
            </div>
        );
    }
}
挂载

当组件实例被创建并插入DOM 中时,生命周期调用顺序如下:

  • constructor()
  • static getDerivedStateFromProps()
  • render()
  • componentDidMount()

下述生命周期方法即将过时,在新代码中应该避免使用它们:
UNSAFE_componentWillMount()

更新

当组件的 props 或 state 发生变化时会触发更新。组件更新的生命周期调用顺序如下:

  • static getDerivedStateFromProps()
  • shouldComponentUpdate()
  • render()
  • getSnapshotBeforeUpdate()
  • componentDidUpdate()

下述方法即将过时,在新代码中应该避免使用它们:
UNSAFE_componentWillUpdate()
UNSAFE_componentWillReceiveProps()

更新

当组件从 DOM 中移除时会调用如下方法:

  • componentWillUnmount()
错误处理

当渲染过程,生命周期,或子组件的构造函数中抛出错误时,会调用如下方法:

  • static getDerivedStateFromError()
  • componentDidCatch()
其他 APIs
  • setState()
  • forceUpdate()
class 属性
  • defaultProps
  • displayName
实例属性
  • props
  • state
常用生命周期方法
render():
render()

render() 方法是 class 组件中唯一必须实现的方法。

当 render 被调用时,它会检查 this.props 和 this.state 的变化并返回以下类型之一:

  • React 元素。通常通过 JSX 创建。例如,<div />会被 React 渲染为 DOM 节点,<Welcome/> 会被 React 渲染为自定义组件,无论是 <div /> 还是 <Welcome/> 均为 React 元素。
  • 数组或 fragments。 使得 render 方法可以返回多个元素。欲了解更多详细信息,请参阅 fragments 文档。
  • Portals。可以渲染子节点到不同的 DOM 子树中。(Portals是一个可以插入指定dom节点下的element元素)欲了解更多详细信息,请参阅有关 portals 的文档
  • 字符串或数值类型。它们在 DOM 中会被渲染为文本节点
  • 布尔类型或 null。什么都不渲染。(主要用于支持返回 test && 的模式,其中 test 为布尔类型。)

render() 函数应该为纯函数,这意味着在不修改组件 state 的情况下,每次调用时都返回相同的结果,并且它不会直接与浏览器交互。
如需与浏览器进行交互,请在 componentDidMount() 或其他生命周期方法中执行你的操作。保持 render() 为纯函数,可以使组件更容易思考。

在组件更新阶段,执行render之前会调用shouldComponentUpdate()方法, 如果 shouldComponentUpdate() 返回 false,则不会调用 render()。

constructor()
constructor(props)

如果不初始化 state 或不进行方法绑定,则不需要为 React 组件实现构造函数。
在 React 组件挂载之前,会调用它的构造函数。在为 React.Component 子类实现构造函数时,应在其他语句之前前调用 super(props)。否则,this.props 在构造函数中可能会出现未定义的 bug。

通常,在 React 中,构造函数仅用于以下两种情况:

  • 通过给 this.state 赋值对象来初始化内部 state。
  • 为事件处理函数绑定实例

在 constructor() 函数中**不要调用 setState() **方法。如果组件需要使用内部 state,请直接在构造函数中为 this.state 赋值初始 state: 只能在构造函数中直接为 this.state 赋值。如需在其他方法中赋值,你应使用 this.setState() 替代。

constructor(props) {
  super(props);
  // 不要在这里调用 this.setState()
  this.state = { counter: 0 };
  this.handleClick = this.handleClick.bind(this);
}
componentDidMount()()
componentDidMount()

componentDidMount() 会在组件挂载后(插入 DOM 树中)立即调用。依赖于 DOM 节点的初始化应该放在这里。如需通过网络请求获取数据,此处是实例化请求的好地方。

这个方法是比较适合添加订阅的地方。如果添加了订阅,请不要忘记在 componentWillUnmount() 里取消订阅

可以在 componentDidMount() 里直接调用 setState()。它将触发额外渲染,但此渲染会发生在浏览器更新屏幕之前。如此保证了即使在 render() 两次调用的情况下,用户也不会看到中间状态。请谨慎使用该模式,因为它会导致性能问题。通常,你应该在 constructor() 中初始化 state。如果渲染依赖于 DOM 节点的大小或位置,比如实现 modals 和 tooltips 等情况下,你可以使用此方式处理

componentDidUpdate()
componentDidUpdate(prevProps, prevState, snapshot)

componentDidUpdate() 会在更新后会被立即调用。首次渲染不会执行此方法。

当组件更新后,可以在此处对 DOM 进行操作。如果你对更新前后的 props 进行了比较,也可以选择在此处进行网络请求。(例如,当 props 未发生变化时,则不会执行网络请求)。

componentDidUpdate(prevProps) {
  // 典型用法(不要忘记比较 props):
  if (this.props.userID !== prevProps.userID) {
    this.fetchData(this.props.userID);
  }
}

你也可以在 componentDidUpdate() 中直接调用 setState(),但请注意它必须被包裹在一个条件语句里,正如上述的例子那样进行处理,否则会导致死循环。它还会导致额外的重新渲染,虽然用户不可见,但会影响组件性能。不要将 props “镜像”给 state,请考虑直接使用 props。
如果组件实现了 getSnapshotBeforeUpdate()生命周期(不常用),则它的返回值将作为 componentDidUpdate() 的第三个参数 “snapshot” 参数传递。否则此参数将为 undefined。

如果 shouldComponentUpdate() 返回值为 false,则不会调用 componentDidUpdate()


版权声明:本文为qq_40600414原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
THE END
< <上一篇
下一篇>>