1

I am getting an infinite loop and it is constantly fetching information from the API.

The issue can be solved if I wrap fetchTheMovie() inside an if statement but I do not understand the reason, any ideas or any better solution?

if (!movie.Title) {
  fetchTheMovie(id);
}

The endpoint is /movie/id

Movie Component:

const SingleMovie = (props) => {
  const { id } = useParams();
  const {movie} = props;

  useEffect(() => {
    const {fetchTheMovie} = props;
    fetchTheMovie(id);
  }, []);
}

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(WithSpinner(SingleMovie));

WithSpinner Component:

const WithSpinner = (WrappedComponent) => {
  const Spinner = ({ isLoading, ...otherProps }) => {
    return isLoading ? (
      <SpinnerOverlay>
        <SpinnerContainer />
      </SpinnerOverlay>
    ) : (
      <WrappedComponent {...otherProps} />
    );
  };
  return Spinner;
};

Page that the component is used

const Page = ({ loading }) => {
  const { id } = useParams();
  return (
    <div>
      <SingleMovieComp isLoading={loading} />
    </div>
  );
};

Action:

export function fetchMovieStart() {
  return {
    type: SingleMovieActionTypes.FETCH_MOVIE_START,
  };
}

export function fetchMovieSuccess(movie) {
  return {
    type: SingleMovieActionTypes.FETCH_MOVIE_SUCCESS,
    payload: movie,
  };
}

export function fetchMovieError(error) {
  return {
    type: SingleMovieActionTypes.FETCH_MOVIE_ERROR,
    payload: error,
  };
}

export const fetchMovieAsync = (movieId) => {
  return (dispatch) => {
    dispatch(fetchMovieStart());
    fetch(`URL`)
      .then((response) => response.json())
      .then((movie) => dispatch(fetchMovieSuccess(movie)))
      .catch((error) => dispatch(fetchMovieError(error.message)));
  };
};

Reducer:

const INITIAL_STATE = {
  loading: false,
  movie: {},
};

const singleMovieReducer = (state = INITIAL_STATE, action) => {
  switch (action.type) {
    case SingleMovieActionTypes.FETCH_MOVIE_START: {
      return {
        ...state,
        movie: {},
        loading: true,
      };
    }
    case SingleMovieActionTypes.FETCH_MOVIE_SUCCESS: {
      return {
        ...state,
        movie: action.payload,
        loading: false,
      };
    }
    case SingleMovieActionTypes.FETCH_MOVIE_ERROR: {
      return {
        ...state,
        loading: false,
      };
    }
    default:
      return state;
  }
};
IonKat
  • 216
  • 4
  • 10

1 Answers1

1

You can try the following:

const ComponentWithSpinner = WithSpinner(SingleMovie);
//create single movie container
const SingleMovieContainer = (props) => {
  const { id } = useParams();
  const { fetchTheMovie } = props;
  //remove the useEffect from SingleMovie
  useEffect(() => {
    fetchTheMovie(id);
    //you need to add these dependencies
  }, [fetchTheMovie, id]);
  //Here you could try to use useSelector to get the movie
  //  and pass to SingleMovie but I think you already do
  //  that somewhere else
  //return component with spinner so SingleMovieContainer
  // will not unmount when you load movie
  return <ComponentWithSpinner {...props} />;
};

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(SingleMovieContainer);

Problem is that SingleMovie is unmounted when you load a movie and gets mounted when loading is false, that causes loading to be set true (fetchMovie is dispatched) so it is unmounted again.

HMR
  • 30,349
  • 16
  • 67
  • 136
  • Could you explain how useMemo will enhance the performance? – IonKat Jan 02 '21 at 22:47
  • @IonKat The useMemo is not needed, I updated the answer. You can use that if you pass the component the container needs to render in props. – HMR Jan 03 '21 at 07:09