如何优化该React+Firebase函数组件?解决重复渲染与点击更新异常
问题原因与修复方案
问题1:console.log("run!")执行1000次的原因
- 核心触发逻辑:你在遍历1000条Firebase快照的
forEach循环内,每次都调用setPopularMovies更新状态。React中每次状态更新都会触发组件重渲染,1000次状态更新对应1000次重渲染,组件顶层的console.log每次重渲染都会执行,自然就执行了1000次。 - 修复逻辑:先将所有数据组装为完整数组,一次更新状态即可,避免循环内多次触发重渲染。
问题2:点击喜欢按钮隔一次才更新视图的原因
两个错误共同导致该现象:
- 更新
render状态时用了后缀自增运算符render++:后缀自增会先返回变量的原始值,再给变量本身加1,因此setRender(render++)实际传给更新函数的是旧值,React判断状态无变化就不会触发重渲染。第二次点击时变量已经是自增后的值,才会触发一次更新,以此类推就出现了隔一次生效的现象。 - 你在
likedMovie函数内直接修改普通变量index和movie:这些变量不属于React状态,修改后不会触发视图更新,只有组件重渲染时才会重新计算得到新的随机电影值。
额外优化:
render状态完全多余,你不需要每次点击按钮都重新拉取全量1000条电影数据,初始拉取一次就足够后续随机取值使用。
修复后完整代码
import React, { useState, useEffect } from 'react'; import './App.css'; import firebase from 'firebase/compat/app'; import 'firebase/database'; import 'firebase/firestore'; function RateMovies() { const popularMoviesRef = firebase.database().ref("/popular_movies"); const [popularMovies, setPopularMovies] = useState([]); // 新增随机触发器,替代原有无用的render状态,仅用于触发重渲染生成新随机电影 const [randomTrigger, setRandomTrigger] = useState(false); console.log("run!"); useEffect(() => { popularMoviesRef.once("value", function(snapshot){ // 先组装完整数组,再一次更新状态,避免多次重渲染 const movies = [] snapshot.forEach(function(childSnapshot){ const key = childSnapshot.key; const data = childSnapshot.val(); movies.push({ key: key, movieTitle: data.movie_title, moviePoster: data.movie_poster, movieYear: data.movie_year, movieGenre: data.movie_genre, movieRating: data.imdb_rating }) }) setPopularMovies(movies) }); }, []); // 空依赖,初始仅拉取一次数据即可 // 用数组长度替代写死的1000,避免数据不足时取到空值 const index = Math.floor(Math.random() * popularMovies.length); const movie = popularMovies[index]; const auth = firebase.auth(); const db = firebase.firestore(); const user_id = auth.currentUser.uid; function likedMovie() { db.collection('users').doc(user_id).update({ "Rated.liked": firebase.firestore.FieldValue.arrayUnion({ key: movie.key, movieTitle: movie.movieTitle, moviePoster: movie.moviePoster, movieYear: movie.movieYear, movieGenre: movie.movieGenre, movieRating: movie.movieRating, }), }) .then(function(){ console.log("Successfully updated!"); // 切换触发器状态,触发重渲染自动生成新的随机电影 setRandomTrigger(prev => !prev); }); } return ( <div> <p>Rate Movies Here</p> <div> {movie?.movieTitle} </div> <div> {movie?.movieYear} </div> <br/> <button onClick={likedMovie}> Like</button> <button> Dislike</button> <br/> <br/> {/* React中class属性需写为className */} <img className="rate-image" src = {`${movie?.moviePoster}`} /> </div> ) } export default RateMovies;
内容的提问来源于stack exchange,提问作者Kris
相关产品推荐
相关产品推荐

