Home Reference Source

src/core/dependencies/withObserveDependency.js

import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { isEmpty, isFunction, map } from 'lodash';
import observeStore from '../../utils/observeStore';
import { setWatchDependency } from '../../components/widgets/Form/utils';
import { DEPENDENCY_TYPES } from '../dependencyTypes';

export default config => WrappedComponent => {
  class ReRenderComponent extends Component {
    constructor(props) {
      super(props);

      this._observers = [];

      this.observeState = this.observeState.bind(this);
      this.setComponentRef = this.setComponentRef.bind(this);
      this.fetchDependencyAction = this.fetchDependencyAction.bind(this);
      this.reRenderDependencyAction = this.reRenderDependencyAction.bind(this);
      this.setObserveState = this.setObserveState.bind(this);
      this.unsubscribe = this.unsubscribe.bind(this);
      this.setReRenderRef = this.setReRenderRef.bind(this);
    }

    componentDidMount() {
      this.dependencyActions = {
        fetch: this.fetchDependencyAction,
        reRender: this.reRenderDependencyAction,
      };

      this.setObserveState();
    }

    componentWillUnmount() {
      if (!isEmpty(this._observers)) {
        this.unsubscribe();
      }
    }

    fetchDependencyAction() {
      config.onChange.apply(this._componentRef, [
        this.props,
        DEPENDENCY_TYPES.fetch,
      ]);
    }

    reRenderDependencyAction() {
      this._reRenderRef && this._reRenderRef.forceUpdate();
      if (isFunction(config.onChange)) {
        config.onChange.apply(this._componentRef, [this.props]);
      }
    }

    setComponentRef(el) {
      this._componentRef = el;
    }

    setReRenderRef(el) {
      this._reRenderRef = el;
    }

    observeState(dependencyType, dependencyAction) {
      const { store } = this.context;
      const { dependencySelector } = this.props;
      return observeStore(
        store,
        state => dependencySelector(state, this.props, dependencyType),
        dependencyAction
      );
    }

    setObserveState() {
      const { dependency } = this.props;
      map(dependency, d => {
        if (this.dependencyActions[d.type]) {
          const observer = this.observeState(
            d.type,
            this.dependencyActions[d.type]
          );
          this._observers.push(observer);
        }
      });
    }

    unsubscribe() {
      map(this._observers, o => o());
    }

    render() {
      return (
        <WrappedComponent
          {...this.props}
          setReRenderRef={this.setReRenderRef}
          ref={this.setComponentRef}
        />
      );
    }
  }

  ReRenderComponent.propTypes = {
    dependencySelector: PropTypes.func,
  };

  ReRenderComponent.defaultProps = {
    dependencySelector: setWatchDependency,
  };

  ReRenderComponent.contextTypes = {
    store: PropTypes.object,
  };

  return ReRenderComponent;
};