TL;DR: I am trying to refactor my class-based App component to a Functional component and I am having some unexpected results with the useEffect Hook.
I am fairly new to React and the version of my app to search for movies with the OMDB API was a class based one. It was working well so I wanted to practice some more on working with hooks and context before venturing into redux.
In my class component I had the following code:
componentDidMount = () => {
window.onscroll = this.handleScroll;
};
handleScroll = () => {
if (!this.state.movies.loading) {
if (getScrollDownPercentage(window) > 0.8) {
const nextPage = this.state.movies.page + 1;
this.searchMovies(this.state.movies.searchValue, nextPage);
this.setState({
...this.state,
movies: {
...this.state.movies,
page: nextPage,
},
});
}
}
};
searchMovies = (str, page = 1) => {
const existingMovies =
this.state.movies.data.length && this.state.movies.searchValue === str
? this.state.movies.data
: [];
this.setState({
...this.state,
movies: {
...this.state.movies,
loading: true,
},
});
axios
.get(`${process.env.REACT_APP_ENDPOINT}s=${str}&page=${page}`)
.then((response) => {
if (response.data.Response === "True") {
this.setState({
...this.state,
movies: {
...this.state.movies,
loading: false,
searchValue: str,
data: [...existingMovies, ...response.data.Search],
},
});
}
});
};
After the refactor I have following code which feels less clean (so the opposite of what I was hoping to achieve)
The moment I reach the end of the page I get a non-stop stream of axios calls switching between page 2 and 3
If I'm not wrong it's not working because setState is running asynchronously and I am getting constant updates on my page.
useEffect(() => {
window.onscroll = () => {
handleScroll();
};
if (searchValue) {
console.log(page);
searchMovies(searchValue, page);
}
return () => {
window.removeEventListener("scroll", handleScroll);
};
}, [searchValue, page]);
const handleScroll = () => {
if (!loading && searchValue) {
if (getScrollDownPercentage(window) > 0.7) {
setState({
...state,
movies: {
...state.movies,
page: page + 1,
},
});
}
}
};
const searchMovies = (str, page = 1) => {
const existingMovies = data.length && prevSearchValue === str ? data : [];
setState({
...state,
movies: {
...state.movies,
loading: true,
},
});
axios
.get(`${process.env.REACT_APP_ENDPOINT2}s=${str}&page=${page}`)
.then((response) => {
if (response.data.Response === "True") {
setState({
...state,
movies: {
...state.movies,
loading: false,
searchValue: str,
data: [...existingMovies, ...response.data.Search],
},
});
} else {
setState({
...state,
movies: {
...state.movies,
loading: false,
error: true,
},
});
}
});
};
I hope I included everything (and also not too much) to make my clear my problem. Looking out for some React wizards.
Edit: specified problem a little bit more and put it in bold
Given your reply, I think what you'd want to do multiple useEffects
: one for searchMovies and one for the scroll setup.
useEffect(() => {
window.onscroll = () => {
handleScroll();
};
return () => {
window.removeEventListener("scroll", handleScroll);
};
}, []);
useEffect(() => {
if (searchValue) {
console.log(page);
searchMovies(searchValue, page);
}
}, [searchValue, page])
A quick fix will be use an empty array as the second argument to avoid infinite loop
useEffect(() => {
//your code
},[]);
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.