Search code examples
javascriptreactjsreduxreact-hooks

what i can do for changing state without re-render


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.


Solution

  • Issue

    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.

    Solution

    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>