Reputation: 323
import { useEffect, useState } from "react";
import { useSelector, useDispatch } from "react-redux";
import { fetchMovies } from "../../feautures/movies/moviesSlice";
import { BiLeftArrow, BiRightArrow } from "react-icons/bi";
import Card from "../Card/Card";
import Slider from "../UI/Slider/Slider";
import Navigation from "../Navigations/Navigation";
import "./MoviesList.scss";
import request from "../../requests";
const MoviesList = () => {
const dispatch = useDispatch();
// Current Page
const [currentPage, setCurrentPage] = useState(1);
// Handle movies states
const moviesStatus = useSelector((state) => state.movies.status);
const moviesState = useSelector((state) => state.movies.movies);
const moviesError = useSelector((state) => state.movies.error);
const moviesHeading = useSelector((state) => state.movies.moviesHeading); // It's for pagination
// Handle header input
const inputValue = useSelector((state) => state.movies.inputValue);
// Handle movies heading
const moviesMoviesHeading = useSelector(
(state) => state.movies.moviesHeading
);
// Movies according input values
const filteredMovie = moviesState.filter((movie) =>
movie.original_title.toLowerCase().includes(inputValue)
);
// Handle Page Number
const handlePageNumber = (rotation) => {
if (rotation === "goPrev" && currentPage >= 2) {
setCurrentPage((prev) => prev - 1);
console.log("current page: ", currentPage);
} else if (rotation === "goNext" && currentPage < 10) {
setCurrentPage((prev) => prev + 1);
console.log("current page: ", currentPage);
}
};
// Handle Pagination
const prevNextPage = () => {
if (moviesHeading === "POPULAR") {
console.log("current page", currentPage)
dispatch(fetchMovies(request.fetchPopular(currentPage)));
} else if (moviesHeading === "NOW PLAYING") {
console.log("current page", currentPage)
dispatch(fetchMovies(request.fetchPopular(currentPage)));
} else if (moviesHeading === "UP COMING") {
console.log("current page", currentPage)
dispatch(fetchMovies(request.fetchUpComing(currentPage)));
}
};
useEffect(() => {
if (moviesStatus === "idle") {
dispatch(fetchMovies(request.fetchPopular()));
}
}, [dispatch, moviesStatus]);
let content;
if (moviesStatus === "loading") {
<div>selamlar</div>;
} else if (moviesStatus === "succeeced") {
content = (
<div className="movies__container">
<BiLeftArrow
className="movies__arrow movies__arrow--left"
onClick={() => {
handlePageNumber("goPrev")
prevNextPage()
}}
/>
{filteredMovie.map((movie) => {
return <Card movie={movie} key={movie.id} />;
})}
<BiRightArrow
className="movies__arrow movies__arrow--right"
onClick={() => {
handlePageNumber("goNext")
prevNextPage()
}}
/>
</div>
);
} else if (moviesStatus === "failed") {
content = <div>{moviesError}</div>;
}
return (
<div className="movies">
<Slider />
<div className="movies__heading">{moviesMoviesHeading}</div>
<Navigation />
{content}
</div>
);
};
export default MoviesList;
Hi guys have a good day.I try to make movie website with react-redux.But i stuck somewhere.When i click to right arrow button handlePageNumber
function increase currentPage then i pass this value to dispatch(fetchMovies(request.fetchPopular(currentPage)))
to go the next page.But when i click to right arrow button currentPage
state still has 1 value.if click one more time currentPage
has 2 value.Why doesn't it have a value of 2 when I first click it ? I guess value change after re-render if so what can i do ? Finally, if there is something bad in my code, I would appreciate it if you could tell me how i can code clean.
Upvotes: 1
Views: 296
Reputation: 202836
The issue is that React state updates are asynchronously processed, so the state update enqueued in handlePageNumber("goPrev")
or handlePageNumber("goNext")
hasn't processed yet when prevNextPage()
is called on the next line, so currentPage
is still the value from the current render cycle.
See this SO answer (useState set method not reflecting change immediately) for explanation.
You can call the prevNextPage()
function from a useEffect
with a dependency on currentPage
. Since prevNextPage
isn't called elsewhere you can either define it within the useEffect
callback and invoke it, or just move the old logic into the body of the useEffect
callback. You'll need to include moviesHeading
as a dependency as well.
useEffect(() => {
if (moviesHeading === "POPULAR") {
console.log("current page", currentPage)
dispatch(fetchMovies(request.fetchPopular(currentPage)));
} else if (moviesHeading === "NOW PLAYING") {
console.log("current page", currentPage)
dispatch(fetchMovies(request.fetchPopular(currentPage)));
} else if (moviesHeading === "UP COMING") {
console.log("current page", currentPage)
dispatch(fetchMovies(request.fetchUpComing(currentPage)));
}
}, [currentPage, dispatch, moviesHeading]);
And remove the call from the click handlers.
content = (
<div className="movies__container">
<BiLeftArrow
className="movies__arrow movies__arrow--left"
onClick={() => {
handlePageNumber("goPrev");
}}
/>
{filteredMovie.map((movie) => {
return <Card movie={movie} key={movie.id} />;
})}
<BiRightArrow
className="movies__arrow movies__arrow--right"
onClick={() => {
handlePageNumber("goNext");
}}
/>
</div>
I might also suggest simplifying your handlePageNumber
logic to consume a number value instead of a string, and clamp the value to your page range
// Handle Page Number
const handlePageNumber = (nextPage) => {
setCurrentPage(page => Math.max(1, Math.min(page + nextPage, 10));
};
Add pass 1
or -1
to the callback to go back a page, or forward to next page.
content = (
<div className="movies__container">
<BiLeftArrow
className="movies__arrow movies__arrow--left"
onClick={() => {
handlePageNumber(-1);
}}
/>
{filteredMovie.map((movie) => {
return <Card movie={movie} key={movie.id} />;
})}
<BiRightArrow
className="movies__arrow movies__arrow--right"
onClick={() => {
handlePageNumber(1);
}}
/>
</div>
Upvotes: 3
Reputation: 86
It seems that react's setState method works async but you assumed it is sync method. If I were you I store pagenumber in redux state. I write action to handle clicking prev/next buttons and send next page status (if currentPage = 8 then I send 9 for nextButton, and 7 for prevButton), after triggering this action, I handle the async operation with redux-thunk or redux-saga, and whenever I get the response from the middleware, I call success or fail action for this async operation which will handle next redux state
Upvotes: -1