Umut Palabiyik
Umut Palabiyik

Reputation: 323

how to prevent multiple re-render in react

import { useSelector, useDispatch } from "react-redux";
import { useEffect, useState } from "react";
import { useHistory } from "react-router-dom";
import { IoMdArrowRoundBack } from "react-icons/io";
import axios from "axios";

import { fetchMovies } from "../../feautures/movies/moviesSlice";
import Rating from "../../components/UI/Rating/Rating";
import request from "../../requests";

import "./SingleMoviePage.scss";
import SimilarMovies from "../../components/SimilarMovies/SimilarMovies";


const SingleMoviePage = ({ match }) => {
  const dispatch = useDispatch();
  const [movieDetails, setMovieDetails] = useState({});
  const [movieCredits, setMovieCredits] = useState({});

  const history = useHistory();

  console.log("single rendered")

  // number month to string
  const date = new Date(movieDetails.release_date);
  const dateWithMonthName =
    date.getFullYear() +
    "-" +
    date.toLocaleString("en-EN", { month: "long" }) +
    "-" +
    date.getDay();

  /*  params */
  const movieId = match.params.id;
  const page = match.params.page;
  const genre = match.params.genre;

  /* movies reducer handle */
  const moviesStatus = useSelector((state) => state.movies.status);


  /* base urls */
  const baseImgUrl = "https://image.tmdb.org/t/p/original";
  const movieDetailUrl = `https://api.themoviedb.org/3/movie/${movieId}?api_key=c057c067b76238e7a64d3ba8de37076e&language=en-US`;
  const movieCastUrl = `https://api.themoviedb.org/3/movie/${movieId}/credits?api_key=c057c067b76238e7a64d3ba8de37076e&language=en-US`;

  // go home page
  const goHOme = () => {

    history.goBack()
  };

  // fetch movie cast
  useEffect(() => {
    const fetchMovieCast = async () => {
      let response = await axios.get(movieCastUrl);
      response = response.data;
      setMovieCredits(response);
    };
    fetchMovieCast();


  }, [movieCastUrl]);

  // fetch movie details
  useEffect(() => {
    const fetchMovieDetails = async () => {
      let response = await axios.get(movieDetailUrl);
      response = response.data;
      setMovieDetails(response);
    };

    fetchMovieDetails();
  }, [movieDetailUrl]);

  let content;
  if (moviesStatus === "loading") {
  } else if (moviesStatus === "succeeded") {
    content = (
      <div
        className="single-movie__container"
        style={{
          backgroundImage: `url(${
            movieDetails.backdrop_path
              ? baseImgUrl + movieDetails.backdrop_path
              : baseImgUrl + movieDetails.poster_path
          })`,
        }}
      >
        <div className="single-movie__details">
          <IoMdArrowRoundBack
            className="single-movie__back"
            onClick={goHOme}
            size={65}
            color={"#e50914"}
          />
          <h1 className="single-movie__title">{movieDetails.title}</h1>
          <div className="single-movie__rate">
            <Rating
              rating={movieDetails.vote_average}
              className="single-movie__stars"
            />
          </div>
          <p className="single-movie__overview">{movieDetails.overview}</p>

          <div className="single-movie__informations single-movie__informations--genres">
            <label className="single-movie__informations-heading">Genres</label>
            <div className="single-movie__informations-container">
              {movieDetails.genres?.map((genre) => {
                return <div className="single-movie__info">{genre.name}</div>;
              })}
            </div>
          </div>

          <div className="single-movie__informations single-movie__informations--stars">
            <label className="single-movie__informations-heading">
              Starring
            </label>
            <div className="single-movie__informations-container">
              {movieCredits.cast?.slice(0, 4).map((star) => {
                return <div className="single-movie__info">{star.name}</div>;
              })}
            </div>
          </div>

          <div className="single-movie__informations single-movie__informations--released">
            <label className="single-movie__informations-heading">
              Release Date
            </label>
            <div className="single-movie__informations-container">
              <div className="single-movie__info">{dateWithMonthName}</div>
            </div>
          </div>

          <div className="single-movie__informations single-movie__informations--production">
            <label className="single-movie__informations-heading">
              Production
            </label>
            <div className="single-movie__informations-container">
              {movieDetails.production_countries?.slice(0, 2).map((country) => {
                return <div className="single-movie__info">{country.name}</div>;
              })}
            </div>
          </div>
        </div>
        <SimilarMovies movieId={movieId} />
      </div>
    );
  }

  useEffect(() => {
    if (genre === "POPULAR") {
      dispatch(fetchMovies(request.fetchPopular(page)));
    } else if (genre === "NOW PLAYING") {
      dispatch(fetchMovies(request.fetchNowPlaying(page)));
    } else if (genre === "UP COMING") {
      dispatch(fetchMovies(request.fetchUpComing(page)));
    }
  }, [dispatch, genre, page]);


  return <div className="single-movie">{content}</div>;
};

export default SingleMoviePage;

Hi all.When i clicked Card compenent it navigate me to the SingleMoviePage component.But SingleMoviePage component re-render five times.How can i find this issues's source ? And how can i prevent that ? Finally is there any problem to fetch MovieCast and MovieDetails in same useEffect hook ?

github repo : https://github.com/UmutPalabiyik/hope-movie-app demo : https://hope-movie.web.app/page/1

Upvotes: 0

Views: 3466

Answers (2)

PatAttack
PatAttack

Reputation: 41

The first 2 useEffect hooks fetch data separately which then update your local states, which then triggers re-rendering.

If you don't want to re-render after each data fetch (state update), I'd suggest adding a loading state. Set it to true first and return a loading icon as your content. Then after both movieDetails and movieCredits have been populated, set it to false and return the actual content. This should render twice in total.

Have you considered graphql? GraphQL can combine your api calls into one and it also handles loading state and error state.

Whatever solution you have, re-rendering will happen when you are fetching data. Initial render won't have any data and after fetching data it must re-render.

Upvotes: 1

user3875913
user3875913

Reputation: 252

You should use only one useEffect hook your code is running for all three. React will handle the rest itself.

Upvotes: 0

Related Questions