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.