如何防止多次重新渲染反应
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 加载图标作为您的内容。然后在 movieDetails
和 movieCredits
都被填充后,将其设置为 false 和 return 实际内容。这应该总共渲染两次。
你考虑过graphql吗? GraphQL 可以将您的 api 调用合并为一个,它还可以处理加载状态和错误状态。
无论您有什么解决方案,在获取数据时都会发生重新渲染。初始渲染不会有任何数据,获取数据后必须重新渲染。
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 加载图标作为您的内容。然后在 movieDetails
和 movieCredits
都被填充后,将其设置为 false 和 return 实际内容。这应该总共渲染两次。
你考虑过graphql吗? GraphQL 可以将您的 api 调用合并为一个,它还可以处理加载状态和错误状态。
无论您有什么解决方案,在获取数据时都会发生重新渲染。初始渲染不会有任何数据,获取数据后必须重新渲染。