React点赞功能触发两次数据库数据拉取的原因是什么?
问题原因
你遇到的两次数据库拉取问题是两个错误写法共同导致的:
- 你为了实现点赞后切换电影的需求,额外加了
render状态强制刷新组件,每次render变化都会触发依赖它的useEffect执行,走一遍全量拉取热门电影的逻辑 - 如果你用的是React 18且项目开启了严格模式(默认新建项目都会开),开发环境下组件的副作用会被强制触发两次,用来检测逻辑缺陷,所以拉取请求就变成了两次
另外你当前的实现逻辑本身就有不合理的地方:热门电影列表只需要首次加载的时候拉取一次就存在本地了,完全不需要每次点赞都重新拉取全量数据,你是因为把index声明成了普通变量而非React状态,修改后无法触发组件渲染,才用了强制刷新的错误写法。
修复方案
- 删掉冗余的
render状态,把随机电影索引改成React状态,点赞后直接更新索引触发渲染切换电影,不需要重新拉取数据 - 修复useEffect的依赖,只在组件首次挂载的时候拉取一次热门电影列表
- 修复状态更新的闭包问题,拉取数据后用函数式写法更新电影列表
- 补充你拉取数据时缺失的
movieGenre字段,避免后续调用movie.movieGenre.split时报错
修改后可运行代码
import React, { useState, useEffect } from 'react'; import './App.css'; import firebase from 'firebase/compat/app'; import 'firebase/database'; import 'firebase/firestore'; import './Menu/Menu.css'; function RateMovies() { const popularMoviesRef = firebase.database().ref("/popular_movies"); const [popularMovies, setPopularMovies] = useState([]); // 把索引做成状态,修改后自动触发渲染 const [currentMovieIndex, setCurrentMovieIndex] = useState( Math.floor(Math.random() * 1001) ); const auth = firebase.auth(); const db = firebase.firestore(); const user_id = auth.currentUser?.uid; // 只在首次挂载时拉取一次热门电影列表 useEffect(() => { popularMoviesRef.once("value").then(function(snapshot){ const newArray = [] snapshot.forEach(function(childSnapshot){ const key = childSnapshot.key; const data = childSnapshot.val(); newArray.push({ movieTitle: data.movie_title, moviePoster: data.movie_poster, movieGenre: data.movie_genre // 补充分类字段,避免后续调用split报错 }) }); // 用函数式更新避免闭包陷阱 setPopularMovies(newArray) }); }, []); const movie = popularMovies[currentMovieIndex]; function likedMovie() { if (!movie || !user_id) return; const genres = movie.movieGenre.split(','); db.collection('users').doc(user_id).update({ "Rated.liked": firebase.firestore.FieldValue.arrayUnion({ movieTitle: movie.movieTitle, moviePoster: movie.moviePoster, }) }).then(function(){ // 直接更新索引状态切换电影,不需要重新拉数据 setCurrentMovieIndex(Math.floor(Math.random() * 1001)) }); } return ( <div className="rate-background"> <div className="rate-title"> {movie?.movieTitle} </div> <button onClick={likedMovie}> Like</button> <img className="rate-image" src={movie?.moviePoster} /> </div> ) } export default RateMovies;
补充说明
如果开发环境还有两次执行的情况,属于React严格模式的正常检测行为,打包到线上环境就会自动消失,不需要额外处理。
内容的提问来源于stack exchange,提问作者Kris
相关产品推荐
相关产品推荐

