如何防止多次重新渲染反应

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;

您好all.When我点击了Card组件,它将我导航到SingleMoviePage component.But SingleMoviePage组件重新渲染五个times.How我能找到这个问题的来源吗?我该如何防止呢?最后,在同一个 useEffect 挂钩中获取 MovieCast 和 MovieDetails 有什么问题吗?

github 回购:https://github.com/UmutPalabiyik/hope-movie-app 演示:https://hope-movie.web.app/page/1

您应该只使用一个 useEffect 挂钩,您的代码是 运行 所有三个挂钩。 React 将自行处理其余部分。

前 2 个 useEffect 挂钩分别获取数据,然后更新您的本地状态,然后触发重新渲染。

如果您不想在每次获取数据(状态更新)后重新渲染,我建议添加一个 loading 状态。首先将其设置为 true,然后 return 加载图标作为您的内容。然后在 movieDetailsmovieCredits 都被填充后,将其设置为 false 和 return 实际内容。这应该总共渲染两次。

你考虑过graphql吗? GraphQL 可以将您的 api 调用合并为一个,它还可以处理加载状态和错误状态。

无论您有什么解决方案,在获取数据时都会发生重新渲染。初始渲染不会有任何数据,获取数据后必须重新渲染。