美文网首页
高阶组件(HOC)

高阶组件(HOC)

作者: hanxianshe_9530 | 来源:发表于2019-10-25 11:07 被阅读0次

React官方定义高阶组件的概念是:

A higher-order component is a function that takes a component and returns a new component.

通常情况下,实现高阶组件的方式有以下两种:
属性代理(Props Proxy)
反向继承(Inheritance Inversion)

属性代理

实质上是通过包裹原来的组件来操作props,举个简单的例子:

import React, { Component } from 'React';
//高阶组件定义
const HOC = (WrappedComponent) =>
  class WrapperComponent extends Component {
    render() {
      return <WrappedComponent {...this.props} />;
    }
}
//普通的组件
class WrappedComponent extends Component{
    render(){
        //....
    }
}

//高阶组件使用
export default HOC(WrappedComponent)

我们可以看见函数HOC返回了新的组件(WrapperComponent),这个组件原封不动的返回作为参数的组件(也就是被包裹的组件:WrappedComponent),并将传给它的参数(props)全部传递给被包裹的组件(WrappedComponent)。这么看起来好像并没有什么作用,其实属性代理的作用还是非常强大的。

操作props

我们看到之前要传递给被包裹组件WrappedComponent的属性首先传递给了高阶组件返回的组件(WrapperComponent),这样我们就获得了props的控制权(这也就是为什么这种方法叫做属性代理)。我们可以按照需要对传入的props进行增加、删除、修改(当然修改带来的风险需要你自己来控制),举个例子:

const HOC = (WrappedComponent) =>
    class WrapperComponent extends Component {
        render() {
            const newProps = {
                name: 'HOC'
            }
            return <WrappedComponent
                {...this.props}
                {...newProps}
            />;
        }
    }

在上面的例子中,我们为被包裹组件(WrappedComponent)新增加了固定的name属性,因此WrappedComponent组件中就会多一个name的属性。

抽象state

属性代理的情况下,我们可以将被包裹组件(WrappedComponent)中的状态提到包裹组件中,一个常见的例子就是实现不受控组件到受控的组件的转变

class WrappedComponent extends Component {
    render() {
        return <input name="name" {...this.props.name} />;
    }
}

const HOC = (WrappedComponent) =>
    class extends Component {
        constructor(props) {
            super(props);
            this.state = {
                name: '',
            };

            this.onNameChange = this.onNameChange.bind(this);
        }

        onNameChange(event) {
            this.setState({
                name: event.target.value,
            })
        }

        render() {
            const newProps = {
                name: {
                    value: this.state.name,
                    onChange: this.onNameChange,
                },
            }
            return <WrappedComponent {...this.props} {...newProps} />;
        }
    }

上面的例子中通过高阶组件,我们将不受控组件(WrappedComponent)成功的转变为受控组件.

用其他元素包裹组件

    render(){
        <div>
            <WrappedComponent {...this.props} />
        </div>
    }

这种方式将被包裹组件包裹起来,来实现布局或者是样式的目的。

在属性代理这种方式实现的高阶组件,以上述为例,组件的渲染顺序是: 先WrappedComponent再WrapperComponent(执行ComponentDidMount的时间)。而卸载的顺序是先WrapperComponent再WrappedComponent(执行ComponentWillUnmount的时间)。

高阶组件的用法,其实就是封装个函数将传入的组件添加上数据,直接导出即可,我们常用的react-redux 中的 connect(Children) 一个道理,封装完将数据导入到组件当中,组件相应的具有数据,以及具有了dispatch方法,就是这么个封装。
话不多说直接上个小栗子:

class Parents extends Component {
  constructor(props) {
    super(props);
      this.state = {
         parentsSourse: '我是父组件数据'
      }
  }
  render() {
    <>
      <Children />
      这是父组件,相当于我们的外层组件
    </>  
  }    
}    
class Children  extends Component {
   render() {
      <>
         这是子组件,我们展示组件
      </>  
   }    
}

我们假如我们想让父组件包含的组件都具有一个属性值,这个值是 newType: true, 此时我们可以直接向下级 Childlren 传递,那么我们也可以封装下父组件导出个高阶组件,那么这个方法可以这么写:

const Hoc_component = (HocCompoent) =>  {
   return  class NewComponent extends React.Component{
      constructor(props){
         super(props);
         this.state={}
      }
    
       render() {
            const  props = { newType: true } 
            return <HocCompoent {...this.props}  {...props}/>
       }
   }
}    

// 此时所有的组件只要使用

Hoc_component(Children);   // 此时的子组件就具有了这个方法包装的 newType属性,我们可以去打印看下。

下面的例子,我们把两个组件相似的生命周期方法提取出来,通过包装,能够节省非常多的重复代码。

// CommentList
class CommentList extends React.Component {
  constructor(props) {
    super(props);
    this.handleChange = this.handleChange.bind(this);
    this.state = {
      // "DataSource" is some global data source
      comments: DataSource.getComments()
    };
  }

  componentDidMount() {
    // Subscribe to changes
    DataSource.addChangeListener(this.handleChange);
  }

  componentWillUnmount() {
    // Clean up listener
    DataSource.removeChangeListener(this.handleChange);
  }

  handleChange() {
    // Update component state whenever the data source changes
    this.setState({
      comments: DataSource.getComments()
    });
  }

  render() {
    return (
      <div>
        {this.state.comments.map((comment) => (
          <Comment comment={comment} key={comment.id} />
        ))}
      </div>
    );
  }
}
// BlogPost
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 <TextBlock text={this.state.blogPost} />;
  }
}

他们虽然是两个不同的组件,对DataSource的需求也不同,但是他们有很多的内容是相似的:

  • 在组件渲染之后监听DataSource
  • 在监听器里面调用setState
  • 在unmout的时候删除监听器

在大型的工程开发里面,这种相似的代码会经常出现,那么如果有办法把这些相似代码提取并复用,对工程的可维护性和开发效率可以带来明显的提升。
使用HOC我们可以提供一个方法,并接受不了组件和一些组件间的区别配置作为参数,然后返回一个包装过的组件作为结果。

function withSubscription(WrappedComponent, selectData) {
  // ...and returns another component...
  return class extends React.Component {
    constructor(props) {
      super(props);
      this.handleChange = this.handleChange.bind(this);
      this.state = {
        data: selectData(DataSource, props)
      };
    }

    componentDidMount() {
      // ... that takes care of the subscription...
      DataSource.addChangeListener(this.handleChange);
    }

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

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

    render() {
      // ... and renders the wrapped component with the fresh data!
      // Notice that we pass through any additional props
      return <WrappedComponent data={this.state.data} {...this.props} />;
    }
  };
}

然后我们就可以通过简单的调用该方法来包装组件:

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

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

注意:在HOC中我们并没有修改输入的组件,也没有通过继承来扩展组件。HOC是通过组合的方式来达到扩展组件的目的,一个HOC应该是一个没有副作用的方法。

相关文章

  • React 进阶之高阶组件

    高阶组件 HOC 高阶组件(HOC)是react中的高级技术,用来重用组件逻辑。但高阶组件本身并不是React A...

  • ReactNative中的高阶组件(HOC)和继承详解

    ReactNative中的高阶组件(HOC)和继承详解 共同点: 高阶组件(HOC)是 React 中用于复用组件...

  • 高阶组件

    Hoc(高阶组件) 概念 hoc基本用法 hoc链式调用 hoc装饰器用法 概念 概念: 接受组件, 返回新组件,...

  • React 高阶组件(HOC)

    什么是高阶组件? 高阶组件(Higher-Order Components,简称HOC):简而言之,高阶组件就是加...

  • React高阶组件(HOC)

    高阶组件(Higher-Order Components) 高阶组件(HOC)是 React 中用于重用组件逻辑的...

  • 高阶组件

    React 高阶组件HOC (Higher-Order Component) 高阶组件是react中重复使用组件逻...

  • React进阶篇(一)高阶组件

    高阶组件 高阶组件(Higher Order Component,HOC)是React的一种设计模式,用于增强现有...

  • 从高阶函数到高阶组件

    介绍 高阶组件(HOC)是 React 中用于复用组件逻辑的一种高级技巧。HOC 自身不是 React API 的...

  • React 学习笔记二 - HOC 高阶组件理解

    官方定义 高阶组件(HOC)是 React 中用于 复用组件逻辑 的一种高级技巧。HOC 自身不是 React A...

  • 高阶组件

    higher-order-component (高阶组件HOC)类似于高阶函数,它接受一个React组件作为参数,...

网友评论

      本文标题:高阶组件(HOC)

      本文链接:https://www.haomeiwen.com/subject/jxksvctx.html