alodium
alodium

Reputation: 135

Higher-Order Component Being Called Multiple Times

I've created the following Higher-Order Component:

const baseComponent = (WrappedComponent: React.ComponentClass<any>) => (props: IKeyValue) => {
  if (props.isLoading) {
    return (
      <LoadingSpinnerComponent shown={true} />
    );
  }

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

This is causing this component to be called indefinitely. (componentDidMount in myComponent will, means, this component is being recreated).

I am using it in the following way:

export default connect(
  mapStateToProps,
  dispatchToProps,
)(baseComponent(myComponent)) as React.ComponentClass<any>;

When removing the if part:

  if (props.isLoading) {
    return (
      <LoadingSpinnerComponent shown={true} />
    );
  }

The component will be called only once. I tried to debug deep inside the react lib code but the magic is a wonder for me. Any idea?

Thanks in advance!

EDIT:

The full code: myComponent:

class MiniCatalogContainer extends React.PureComponent<IProps, void> {
  public componentDidMount() {
    const { fetchCatalog} = this.props;
    fetchCatalog({path});
  }

  public render() {
    ...
  }
}

export default connect(
  mapStateToProps,
  dispatchToProps,
)(baseComponent(MyComponent)) as React.ComponentClass<any>;

promise-middleware

import { Dispatch } from 'react-portal/src/interfaces';
import isPromise from 'react-portal/src/utils/is-promise';

interface IOptions {
  dispatch: Dispatch;
}

export default function promiseMiddleware({ dispatch }: IOptions) {
  return (next: Dispatch) => (action: any) => {
    if (!isPromise(action.payload)) {
      return next(action);
    }

    const { types, payload, meta } = action;
    const { promise, data } = payload;
    const [ PENDING, FULFILLED, REJECTED ] = types;

   /**
    * Dispatch the pending action
    */
    dispatch( { type: PENDING,
                ...(data ? { payload: data } : {}),
                ...(meta ? { meta } : {}),
    });

    /**
     * If successful, dispatch the fulfilled action, otherwise dispatch
     * rejected action.
     */
    return promise.then(
      (result: any) => {
        dispatch({
          meta,
          payload: result,
          type: FULFILLED,
        });
      },
      (error: any) => {
        dispatch({
          meta,
          payload: error,
          type: REJECTED,
        });
      },
    );
  };
}

SOLUTION:

As @Shleng noticed, the loop is because of my fetch call inside the component. I ended up with the simple solution:

const baseComponent = (WrappedComponent: React.ComponentClass<any>) => (props: IKeyValue) => {
      if (props.isLoading) {
        return (
          <LoadingSpinnerComponent shown={true} />
          <WrappedComponent {...props} />
        );
      }

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

Upvotes: 2

Views: 1056

Answers (1)

rstar
rstar

Reputation: 1006

Because you call fetchCatalog inside wrapped component's componentDidMount, so the workflow results in a loop as illustrated below:

mount WrappedComponent -> fetch -> mount Spinner -> loaded -> mount WrappedComponent -> fetch (loop!)

isLoading: false       -> true                   -> false                            -> true

Upvotes: 2

Related Questions