React-高阶组件(一)

文章目录

  • 一、概念
    • 与普通组件的区别
  • 二、什么时候使用
  • 三、例子
  • 四、使用总结

一、概念

高阶组件(HOC)是 React 中用于复用组件逻辑的一种高级技巧。HOC 自身不是 React API 的一部分,它是一种基于 React
的组合特性而形成的设计模式。

高阶组件本质上就是一个函数(HOC 是纯函数,没有副作用),接收一个组件后返回另外一个新的组件,这么做的意图就是为了增强(enhance),比如装饰、增添行为、增添逻辑等,都可以放在高阶组件中,高阶组件是一种可以增强组件的能力。
具体而言,高阶组件是参数为组件,返回值为新组件的函数。

const EnhancedComponent = higherOrderComponent(WrappedComponent);

HOC 在 React 的第三方库中很常见,例如 Redux 的 connect 和 Relay 的 createFragmentContainer。

conect:连接React组件与 Redux store。

import { connect } from 'react-redux';
import { testCreators } from 'redux';

...
const mapStateToProps = state => {
    return {
       testDate:state.appStore.CommonStore.testDate
    };
}
const mapDispatchToProps = dispatch =>{
    return {
        actions: bindActionCreators(Object.assign({}, testCreators), dispatch)
    }
}

testComponent.contextTypes = {
    router: PropTypes.shape({
        history: PropTypes.object.isRequired,
    })
};

export default connect(mapStateToProps,mapDispatchToProps)(testComponent);

与普通组件的区别

  • 组件:将 props 转换为 UI
  • 高阶组件:将组件转换为另一个组件。

二、什么时候使用

当你的组件之间出现重复的模式 / 逻辑的时候。

  • 挂载,订阅数据
  • 为 UI 增加交互(也可以使用容器组件,或者 Render Props)
  • 排序,过滤输入的数据

三、例子

我们之前建议使用 mixins 用于解决横切关注点相关的问题。但我们已经意识到 mixins 会产生更多麻烦。

组件是 React 中代码复用的基本单元。但你会发现某些模式并不适合传统组件。

例子:

例如,假设有一个 CommentList 组件,它订阅外部数据源,用以渲染评论列表:

class CommentList extends React.Component {
  constructor(props) {
    super(props);
    this.handleChange = this.handleChange.bind(this);
    this.state = {
      // 假设 "DataSource" 是个全局范围内的数据源变量
      comments: DataSource.getComments()
    };
  }

  componentDidMount() {
    // 订阅更改
    DataSource.addChangeListener(this.handleChange);
  }

  componentWillUnmount() {
    // 清除订阅
    DataSource.removeChangeListener(this.handleChange);
  }

  handleChange() {
    // 当数据源更新时,更新组件状态
    this.setState({
      comments: DataSource.getComments()
    });
  }

  render() {
    return (
      
{this.state.comments.map((comment) => ( ))}
); } }

稍后,编写了一个用于订阅单个博客帖子的组件,该帖子遵循类似的模式:

class BlogPost extends React.Component {
  constructor(props) {
    super(props);
    this.handleChange = this.handleChange.bind(this);
    this.state = {
      blogPost: DataSource.getBlogPost(props.id)
    };
  }

  componentDidMount() {
    DataSource.addChangeListener(this.handleChange);
  }

  componentWillUnmount() {
    DataSource.removeChangeListener(this.handleChange);
  }

  handleChange() {
    this.setState({
      blogPost: DataSource.getBlogPost(this.props.id)
    });
  }

  render() {
    return ;
  }
}

CommentList 和 BlogPost 不同 - 它们在 DataSource 上调用不同的方法,且渲染不同的结果。
但它们的大部分实现都是一样的:

  • 在挂载时,向 DataSource 添加一个更改侦听器。
  • 在侦听器内部,当数据源发生变化时,调用 setState。
  • 在卸载时,删除侦听器。

你可以想象,在一个大型应用程序中,这种订阅 DataSource 和调用 setState 的模式将一次又一次地发生。我们需要一个抽象,允许我们在一个地方定义这个逻辑,并在许多组件之间共享它。这正是高阶组件擅长的地方。

对于订阅了 DataSource 的组件,比如 CommentList 和 BlogPost,我们可以编写一个创建组件函数。该函数将接受一个子组件作为它的其中一个参数,该子组件将订阅数据作为 prop。让我们调用函数 withSubscription:

const CommentListWithSubscription = withSubscription(
  CommentList,
  (DataSource) => DataSource.getComments()
);

const BlogPostWithSubscription = withSubscription(
  BlogPost,
  (DataSource, props) => DataSource.getBlogPost(props.id)
);

第一个参数是被包装组件。第二个参数通过 DataSource 和当前的 props 返回我们需要的数据。

当渲染 CommentListWithSubscriptionBlogPostWithSubscription 时, CommentList 和 BlogPost 将传递一个 data prop,其中包含从 DataSource 检索到的最新数据:

// 此函数接收一个组件...
function withSubscription(WrappedComponent, selectData) {
  // ...并返回另一个组件...
  return class extends React.Component {
    constructor(props) {
      super(props);
      this.handleChange = this.handleChange.bind(this);
      this.state = {
        data: selectData(DataSource, props)
      };
    }

    componentDidMount() {
      // ...负责订阅相关的操作...
      DataSource.addChangeListener(this.handleChange);
    }

    componentWillUnmount() {
      DataSource.removeChangeListener(this.handleChange);
    }

    handleChange() {
      this.setState({
        data: selectData(DataSource, this.props)
      });
    }

    render() {
      // ... 并使用新数据渲染被包装的组件!
      // 请注意,我们可能还会传递其他属性
      return ;
    }
  };
}

再来个例子
高阶组件:

//定义高阶组件 //放在common公用方法里面
import React, { Component} from 'react';
export default (WrappedComponent) => {  
    return class extends Component {
          constructor(props) {
            super(props)
            this.state = { //定义可复用的状态
                count:1
            }
            this.getCode = this.getCode.bind(this) 
          }
    
          componentDidMount() {
            alert('111')
          }

        //定义可复用的方法
          getCode(mobile) {
            // ...
            this.setState({count : mobile})
            console.log(mobile)
          }
          postVcode(mobile) {
            // ...
          }
          render() {
            return (
              
) } } } // 高阶组件就是把可复用的逻辑放在定义的高阶组件中,然后把需要调用高阶组件的组件作为参数传给高阶组件,然后在高阶组件中 // 实例化该组件,再把需要复用的方法和state传给次组件,然后在此组件中的props中就可以拿到高阶组件里定义的逻辑方法和state了

传入的组件:

import React, { Component} from 'react';
import HOC from './index'
//高阶组件的使用
class Register extends Component{
       render() {
        return (
          
{this.props.state.count}
) } } export default HOC(Register) //高阶组件其实就是将复用的方法传给组件,使代码简洁、代码量更少

四、使用总结

比较好的做法:

  • 当出现重复的模式的时候,使用它们
  • 为了方便调试,需要更新处理之后组件的 displayName
  • 传递与当前 HOC 无关的所有 props

糟糕的做法:

  • 过度使用,其他模式可能会更加适合
  • 改变原始组件
  • 在 render 方法中使用高阶组件

注意:
永远不要在 React render() 方法中定义 React 组件(甚至是无状态组件)。React 在每次更新状态的时候,都会废弃旧的 html DOM 元素并将其替换为全新的元素。比如在 render() 函数中定义一个输入组件,元素被替换之后就会失去焦点,每次只能输入一个字符。

  • Refs 不会被传递
  • 务必复制静态方法
  • 大部分 HOC 都可以和 render props 相互替换使用

本文链接https://blog.csdn.net/qq_39903567/article/details/115382231

你可能感兴趣的:(React,react,组件化,高阶组件,前端,面试)