JavaScript React Higher-Order Components and Factory Method Implementation

HOC is an abstraction that allows to define data subscription in a single place and share data across many components. HOCs can be used where external data center is required for data fetching and updating as alternative to tricks such as DataCenter.addChangeListener() in componentDidMount and DataCenter.removeChangeListener() in componentWillUnmount.

This article is originally a not of reading React Docs - Higher-Oder Components and re-composed with my understanding and implementation of a HOC factory method.

Definitions

A higher-order component is a function that takes a component and returns a new component such as const EnhancedComponent = higherOrderComponent(WrappedComponent);. HOCs can be thought as parameterized container component definitions. When a HOC is applied to a component, the original component is wrapped with a container component. a HOC doesn’t modify the input component, nor does it use inheritance to copy its behavior. Rather, a HOC composes the original component by wrapping it in a container component with only props-based contract. Therefore, A HOC is a pure function with zero side-effects. The wrapped component receives all the props of the container, along with new prop(s) (e.g., data), which it uses to render its output.

Hint: Whereas A React component transforms props into UI, a higher-order component transforms a component into another component.

Philosophies

Don’t Mutate the Original Component. Use Composition (by wrapping the input component in a container component).

Conventions

Unrelated Props should be Passed Through to the Wrapped Component. Composability Maximizing is recommended by constructing Single-argument HOCs which have the signature Component => Component and enable to be compose-ed. Display Name is recommended to be Wrapped for Easy Debugging by providing a displayName property for HOCs

Caveats

Don’t Use HOCs Inside the render Method considering React's diffing algorithm and updating or replacing algorithm. Non-React Static Methods Aren't Copied Over which can be automatically solved with hoist-non-react-statics. Refs Aren’t Passed Through which can be solved with React.forwardRef API (introduced with React 16.3).

HOC Factory Method

import hoistNonReactStatic from 'hoist-non-react-statics';

function injectDataCenter(DataCenter = {}, convertProps = obj => obj) {
  return (WrappedComponent) => {
    const DataCenterInjectedComponent = class extends React.Component {
      static displayName = `${ DataCenter.displayName || DataCenter.name || 'DataCenter'}Injected(${WrappedComponent.displayName || WrappedComponent.name || 'Component'})`;

      constructor(props) {
        super(props);

        this.handleChange = this.handleChange.bind(this);
        this.state = convertProps(DataCenter);
      }

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

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

      handleChange() {
        this.setState(convertProps(DataCenter));
      }

      render() {
        const { forwardedRef, ...restProps } = this.props;

        return (
          <WrappedComponent {...this.state} {...restProps} ref={forwardedRef} />
        );
      }
    }    

    // NOTE: copy staticMethods of WrappedComponents to DataCenterInjectedComponent
    hoistNonReactStatic(DataCenterInjectedComponent, WrappedComponent);

    // NOTE: use `React.forwardRef` API to copy `ref` of WrappedComponents to DataCenterInjectedComponent

    return React.forwardRef((props, ref) => (<DataCenterInjectedComponent {...props}forwardedRef={ref} />));
  };
}

export default injectDataCenter;

References


* cached version, generated at 2018-12-29 12:02:03 UTC.

Subscribe by RSS