简体   繁体   中英

useEffect is causing an infinite Loop

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;
  }
};

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM