Higher-order components trong reactjs

Giới thiệu

A higher-order component (HOC) is an advanced technique in React for reusing component logic. HOCs are not part of the React API, per se. They are a pattern that emerges from React’s compositional nature.

Higher-order component (HOC) là một pattern trong reactjs, nó được định nghĩa như một function, nó nhận vào một đối số là một component và trả về một component mới.


const EnhancedComponent = higherOrderComponent(WrappedComponent);

Trong bài viết này, chúng ta sẽ thảo luận tại sao higher-order components thì thực sự hữu ích và cách để sử dụng nó.

Ví dụ

Một HOC nó sẽ có pattern như thế này:

// It's a function...
function myHOC() {
  // Which returns a function that takes a component...
  return function(WrappedComponent) {
    // It creates a new wrapper component...
    class TheHOC extends React.Component {
      render() {
        // And it renders the component it was given
        return <WrappedComponent {...this.props} />;
      }
    }

    // Remember: it takes a component and returns a new component
    // Gotta return it here.
    return TheHOC;
  }
}

Bây giờ chúng ta sẽ xem xét một ví dụ, giả sử chúng ta có 2 component thế này: ProductDetails.js

import React, { Component } from 'react';
import { fetchProduct }  from 'actions';  // funtion fetch product 

class ProductDetails extends Component {
  constructor(props) {
    super(props);
    this.state = {
      product: null
    };
  }
  
  componentWillMount() {
      const { fetchProduct } = this.props;
      fetchProduct();
  }

  componentDidMount() {
      const { product } = this.product
      this.setState({ product });
  }

  render() {
    const { product } = this.state;

    if(!product) {
      return <div>Loading...</div>;
    }

    return (
      <div>
        <img src={product.image}/>
        <div>{product.title}</div>
        <div>{product.body}</div>
      </div>
    );
  }
}

export default ProductDetails;

ProductSummary.js

import React, { Component } from 'react';
import { fetchProduct }  from 'actions';  // funtion fetch product 

class ProductSummary extends Component {
 constructor(props) {
    super(props);
    this.state = {
      product: null
    };
  }
  
  componentWillMount() {
      const { fetchProduct } = this.props;
      fetchProduct();
  }

  componentDidMount() {
      const { product } = this.product
      this.setState({ product });
  }

  render() {
    const { product } = this.state;

    if(!product) {
      return <div>Loading...</div>;
    }

    return (
      <div>
        <div>{product.summary}</div>
      </div>
    );
  }
}

export default ProductSummary;

Sau khi nhìn qua 2 component này, bạn sẽ thấy Duplicate code. Trong constructor và component chúng ta đều làm những logic code và state giống nhau. Bây giờ chúng ta có thể áp dụng pattern higer-order-component, chúng ta có thể Reusabble state ở các page khác nhau mà không để bị duplicate code như trên.

Đầu tiên chúng ta xác định các đoạn code logic bị duplicate và các state có thể dùng chung,
Ta tạo 1 HOC ProductLoader.js từ skeleton như bên trên

import { fetchProduct }  from 'actions';  // funtion fetch product 

// It's a function...
function loadProduct() {
  // Which returns a function that takes a component...
  return function(WrappedComponent) {
    // It creates a new wrapper component...
    class ProductLoader extends React.Component {
      // Here's the duplicated code from above:
     constructor(props) {
    super(props);
    this.state = {
      product: null
    };
  }
  
  componentWillMount() {
      const { fetchProduct } = this.props;
      fetchProduct();
  }

  componentDidMount() {
      const { product } = this.product
      this.setState({ product });
  }

      render() {
        const { product } = this.state;

        if(!product) {
          return <div>Loading...</div>;
        }

        return (
          <WrappedComponent
            {...this.props}
            product={product} />
        );
      }
    }

    // Remember: it takes a component and returns a new component
    // Gotta return it here.
    return ProductLoader;
  }
}

export default loadProduct;

Bây giờ product state được handle tron productloader HOC, và nó được truyền xuống WrappedComponent như một props.

Bây giờ chúng ta sử dụng HOC productloader vào 2 component bên trên:

ProductDetails.js

import React, { Component } from 'react';
import loadProduct from './ProductLoader';

class ProductDetails extends Component {
  render() {
    const { product } = this.props;

    return (
      <div>
        <img src={product.image}/>
        <div>{product.title}</div>
        <div>{product.body}</div>
      </div>
    );
  }
}

export default loadProduct()(ProductDetails);

ProductSummary.js

import React, { Component } from 'react';
import loadProduct from './ProductLoader';

class ProductSummary extends Component {
  render() {
   
    const { product } = this.props;

    return (
      <div>
        <div>{product.summary}</div>
      </div>
    );
  }
}

export default loadProduct()(ProductSummary);

Bây giờ bạn sẽ thấy 2 component của chúng ta sẽ không bị duplicate code như trên nữa. một các tuyệt vời để refactor code. Higher order component chính là cách tuyệt vời để giải quyết các logic code và state bị trùng lặp ở các component với nhau.