React高阶组件了解及使用

目录

创建组件

组件的两种类型

二者的使用注意

PureComponent

React.memo

组件复合

高阶组件 

高阶组件的链式调用

高阶组件的装饰器写法

组件通信--上下文


组件的定义:

  1. 理解:用来实现局部功能效果的代码和资源的集合(html/css/js/image等等)
  2. 为什么要用组件: 一个界面的功能更复杂
  3. 作用:复用编码, 简化项目编码, 提高运行效率

React内的组件分为容器组件和展示组件。

这里呢,需要你优先有一个React的项目,在项目内操作,创建项目可查看React框架入门-创建项目、JSX了解

组件概念

React组件分类

  • 函数组件

    • 不具备“状态、ref、周期函数”等内容,第一次渲染完毕后,无法基于组件内部的操作来控制其更新,因此称之为静态组件!

    • 但是具备属性及插槽,父组件可以控制其重新渲染!

    • 渲染流程简单,渲染速度较快!

    • 基于FP(函数式编程)思想设计,提供更细粒度的逻辑组织和复用!

  • 类组件

    • 具备“状态、ref、周期函数、属性、插槽”等内容,可以灵活的控制组件更新,基于钩子函数也可灵活掌控不同阶段处理不同的事情!

    • 渲染流程繁琐,渲染速度相对较慢!

    • 基于OOP(面向对象编程)思想设计,更方便实现继承等!

React Hooks 组件,就是基于 React 中新提供的 Hook 函数,可以让函数组件动态化!

创建组件

简单例子

在 项目src下新建  App.js文件(index.js与App.js同级),作为组件,进入组件后直接输入 rcc 会出现组件的简单框架并导入React及Component,

基本代码如下:

import React, { Component } from 'react'

export default class App extends Component {
  render() {
    return (
      <div>
        App组件
      </div>
    )
  }
}

最后在index.js(入口文件)内引入是使用:

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

ReactDOM.render(
  <App />,
  document.getElementById('root')
);

最后运行会出现

表示组件引入并运行正常!

组件的两种类型

第一种方式:类式组件(容器组件)

声明一个类继承于Component

import React from 'react'

class App extends React.Component{
    render(){
        return <h1>类式组件</h1>
    }
}


//或者
import React, { Component } from 'react'

export default class App extends Component {}

注意:容器组件的使用条件:

  1. 必须要继承父类  extends React.Component
  2. 必须要有render,且render必须有返回值

第二种:函数式组件(展示组件)
基本原则:容器组件负责数据获取,展示组件赋值根据props展示信息

这种方式更加简洁明了

function App() {
  return (
    <div>
      11
    </div>
  );
}

现在这两个写法的具体差异是这样,内容相同的情况下,实现的功能也是一致的。

二者的使用注意

如果你组件的内容内有状态state的变化,会影响到组件视图的变化。例如,组件内的值定时地在变化等必须将组件声明为class类型,因为在class内可以改变其状态声明其状态。

如果这个组件是展示型组件,就可以直接使用函数型组件。

当然,在组件使用时,一定要加上  export default   将组件导出,才可进行使用。

PureComponent

在15的版本出现的组件,纯组件PureComponent,定制了shouldComponentUpdate后的Component(浅比较),实现了一个预定义的比较方式,如果数据没有发生更改,就不会再进行组件的渲染。

因为这里是进行了一个浅比较操作,只比较第一层,所以对象内深层次的数据就无法比较。

例子:

import React from 'react'

class Comment extends React.PureComponent {

}

要使用PureComponent组件的特性应遵循以下几点(需要注意):

  1. 确保数据类型是值类型
  2. 如果是引用类型,保证地址不变(还是同一个对象,不是新的对象),同时不应当有深层次的数据变化

缺点:必须使用类的类型(class形式)

React.memo

React v16.6.0之后的版本可以使用一个新功能React.memo来实现React组件,让函数式组件也有了PureComponent的功能。

const joke = React.memo(() => {
    <div>
        {this.PaymentResponse.value || 'loading...'} 
    </div>
})

这个组件是拥有和纯函数功能的函数组件。

组件复合

组件复合而非继承

组件复合的原则:保证组件功能的单一性。

先举个例子(简单的复合组件):

import React, { Component } from 'react'

function Dialog(props){
  return (
    <div style={{border:`4px solid ${props.color || 'blue'}`}}>
      {/* {props.children}等价于vue内的插槽 */}
        {props.children}
        {/* className="footer" 等价于vue内的具名插槽 */}
        <div className="footer">{props.footer}</div>
    </div>
  )
}
function WelcomeDialog(){
  const confirmBtn = <button onClick={()=>alert('react好!!!')}>确定</button>
  return (
    <Dialog color="green" footer={confirmBtn}>  
      <h1>欢迎光临</h1>
      <p>感谢使用react</p>
    </Dialog>
  );
}
export default class Composition  extends Component {
  render() {
    return (
      <div>
        <WelcomeDialog></WelcomeDialog>
      </div>
    )
  }
}
ReactDOM.render(<Composition />,document.getElementById('root'));

说的是等价于插槽,react内没有插槽的概念。

高阶组件 

提高组件复用率,首先想到的是抽离相同逻辑,在React内有了HOC(Higher-Order Components)的概念

高阶组件其实是个函数,高阶组件也是一个组件,但是他返回另一个组件,产生新的组件对属性进行包装,也可以重写部分生命周期

返回值:返回一个组件

简单来说就是高阶组件就是一个函数,传入一个组件会返回一个全新的组件。

先写一个最简单的高阶组件的例子:

Hoc.js  (位于src下的)

// 定义高阶组件
import React, {Component} from 'react'

function showName(props) {
  return (
      <div>{props.stage} - {props.name}</div>
  );
}
// 高阶组件
const withName = Comp => {
    // 假设通过某种特殊手段来获取名称
    return props => <Comp {...props} name="高阶组件"></Comp>
}

export default withName(showName)

index.js

import Hoc from './components/Hoc';

ReactDOM.render(<Hoc stage="React"/>,document.getElementById('root'));

上面的withName便是高阶组件,会传递给showName组件需要的东西

上述的代码便是简单的高阶组件,高阶组件甚至可以重写组件的生命周期。

这里重写生命周期的操作是在withName高阶组件内进行的操作。

const withName = Comp =>{
  class NewComponent extends Component{
      // 改写生命周期
      componentDidMount () {
        console.log('do something');
      }
      render(){
          return <Comp {...this.props} name="高阶组件的使用介绍"/>
      }
  }
  return NewComponent;
}

高阶组件的链式调用

高阶组件可以链式调用,如下,代码内高阶组件相互嵌套,从内到外执行。

import React, { Component } from 'react'

function ShowName(props){
    return(
        <div>{props.stage}-{props.name}</div>
    )
}
//高阶组件,就是一个函数(扩充简单组件的能力)
const withName = Comp =>{
    class NewComponent extends Component{
        //改写生命周期的能力
        componentDidMount () {
          console.log('do something');
        }
        render(){
            return <Comp {...this.props} name="高阶组件"/>
        }
    }
    return NewComponent;
}

//高阶组件
const withLog = Comp =>{
    console.log(Comp.name + '渲染了');
    return props => <Comp {...props}/>
}

//高阶组件链式调用,由内向外执行(可读性较差)
export default withLog(withName(ShowName));

等于这个组件可以调用另一个高阶组件。

高阶组件的装饰器写法

从上方代码可以看到,链式写法很难以理解,而且写法很不便,ES7内有一个优秀的语法-装饰器,专门处理这种问题。

配置操作如下:

1.要给高阶组件使用装饰器,首先需要安装:

npm install --save-dev babel-plugin-transform-decorators-legacy

在安装完成之后就需要进行配置了,需要在和package.js文件平级的位置添加 config-overrides.js 文件,

2.配置内容如下:

const {injectBabelPlugin} = require('react-app-rewired')

module.exports = function override(config,env){
    //antd的按需加载的写法
    config = injectBabelPlugin([
        'import',
        {libraryName:'antd',libraryDirectory:'es',style:'css'}
    ],config);

    //添加装饰器的能力
    config  = injectBabelPlugin(
        ["@babel/plugin-proposal-decorators",{ legacy :true}],
        config
    );
    return config;
}

代码内的react-app-rewired部分具体安转在Ant Design of React的安装、按需加载的按需加载内,安装即可。

在配置完成后,需要重启才能生效。

3.代码:

import React, { Component } from 'react'

//高阶组件,就是一个函数(扩充简单组件的能力)
const withName = Comp =>{
    class NewComponent extends Component{
        //改写生命周期的能力
        componentDidMount () {
          console.log('do something');
        }
        render(){
            return <Comp {...this.props} name="高阶组件"/>
        }
    }
    return NewComponent;
}

//高阶组件
const withLog = Comp =>{
    console.log(Comp.name + '渲染了');
    return props => <Comp {...props}/>
}

@withLog
@withName
class ShowName extends Component{
    render(){
        return(
            <div>
                {this.props.stage}-{this.props.name}
            </div>
        )
    }
}

//高阶组件链式调用,由内向外执行(可读性较差),使用装饰器无需链式操作
export default ShowName;

注意:

  1. 使用装饰器的组件必须是class组件
  2. 添加装饰器时,必须要在被装饰的组件前被定义好,否则会报错
  3. 添加装饰器时,class组件内不能有导出export

组件通信--上下文

vuejs的provide&inject模式的来源--context

这种模式下有两个角色,一个是Provider,一个是Consumerr,Provider在外部的组件内部需要数据的就用Consumer来读取。

import React, { Component } from 'react'
//组件通信--上下文
//1.创建上下文
const Context = React.createContext();

const store = {
  name :"组件上下文",
  sayHi(){
    console.log(this.name);
  }
}
export default class ContextSample extends Component {
  render() {
    return <Context.Provider value={store}>
        <div>
          {/* 获取数据 */}
          <Context.Consumer>
            {/* 必须嵌套一个函数 */}
            {value => <div onClick={()=>value.sayHi()}>{value.name}</div>}
          </Context.Consumer>
        </div>
    </Context.Provider>
  }
}

Context.Provider标签可以使用点语法进行值的传递,这里标签内一定要有value这个属性进行传值。

获取数据的话,需要使用Context.Consumer将其包裹起来,这里必须内嵌一个函数,拿出Context.Provider传递过来的value值,获取到值后进行渲染输出。

其实有简化的一种写法,可以根据装饰器来进行简化:

import React, { Component } from 'react'

const Context = React.createContext();

const store = {
  name :"组件上下文",
  sayHi(){
    console.log(this.name);
  }
}
//简化写法  
//高阶组件的封装
const withProvider = Comp => props =>{
  <Context.Provider value={store}>
    <Comp>{...props}</Comp>
  </Context.Provider>
}
const withConsumer = Comp => props =>{
      <Context.Consumer>
        {/* 必须嵌套一个函数 */}
        {value => <Comp {...props} value={value}/>}
      </Context.Consumer>
}
@withConsumer
class Inner extends Component{
  render(){
      return(
        <div>{this.props.value.name}</div>
      )
  }
}
@withProvider
class ContextSample extends Component {
  //导出和装饰器不能同时使用,只在ContextSample套一层withProvider
  render() {
      <div><Inner></Inner></div>
  }
}
export default ContextSample;

这种写法是将其分开完成,由外部组件 ContextSample 提供数据store,内部组件进行获取数据,分别对外部组件和内部组件使用withProvider  和withConsumer装饰器,达到简化的作用。

这里简化后的结果,与简化前的一致。

注意:Provider必须和Consumer配套使用

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值