渲染此功能组件的更好方法?

A better way to render this function component?

以下代码的目标是将数据从 firebase 实时数据库提取到名为 popularMovies 的数组中。 (实时数据库中的数据量为1000。)然后从该数组中随机选择一个索引,并将该索引处的电影数据显示在页面上。当用户点击喜欢的按钮时,该电影数据将存储在用户 ID 下的 firestore 文档中,并选择另一个随机索引来显示新电影。

下面的代码大部分都有效,但我注意到 console.log("运行!") 在页面加载时执行了 1000 次(正在读取的数据的大小)以及单击喜欢的按钮时。我希望理解为什么会出现这种情况,因为它在读取数据的 forEach 循环之外。

此外,单击一次按钮时,所有数据都会更新,但显示不会反映更改。但是,再次单击该按钮确实会随着显示再次更新数据(正如我预期的那样是第一次发生)。这继续下去,按钮仅在每隔一次按下后更新显示。我也想知道是什么导致了这个错误。

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] = React.useState([]);
    var [render, setRender] = React.useState(1);

    console.log("run!");

    useEffect(() => {
        
        popularMoviesRef.once("value", function(snapshot){

            snapshot.forEach(function(childSnapshot){
                var key = childSnapshot.key;
                var data = childSnapshot.val();

                setPopularMovies(currMovies => [...currMovies, { 
                    key: key, 
                    movieTitle: data.movie_title, 
                    moviePoster: data.movie_poster, 
                    movieYear: data.movie_year,
                    movieGenre: data.movie_genre, 
                    movieRating: data.imdb_rating
                }])
            });
        });
    }, [render]);

    var index = Math.floor(Math.random() * (1000 + 1));
    var movie = popularMovies[index];

    const auth = firebase.auth();
    var db = firebase.firestore();
    var 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!");
            setRender(render++);
        });

        index = Math.floor(Math.random() * (1000 + 1));
        movie = popularMovies[index];
    }


    return (
        <div>
            <p>Rate Movies Here</p>
            <div> {movie?.movieTitle} </div>
            <div> {movie?.movieYear} </div>
            <br/>
            <button onClick={likedMovie}> Like</button>
            <button> Disike</button>
            <br/>
            <br/>
            <img class="rate-image" src = {`${movie?.moviePoster}`} />
            
        </div>
    )
  
  } 

  export default RateMovies;

您看到 console.log() 1000 次的原因是因为您的 forEach() 循环更新了 RateMovies() 中定义的状态变量 (popularMovies)。每次状态改变时,React 都会重新渲染你的组件。

与其一次添加一个新项目,不如在 forEach 循环中创建一个新数组。这只会修改状态一次,这意味着组件应该只重新渲染一次。

尝试这样的事情:

useEffect(() => {
        
        popularMoviesRef.once("value").then(function(snapshot){
            
            var newArray = []
    
            snapshot.forEach(function(childSnapshot){
                var key = childSnapshot.key;
                var data = childSnapshot.val();

                newArray.push({ 
                    key: key, 
                    movieTitle: data.movie_title, 
                    moviePoster: data.movie_poster, 
                    movieYear: data.movie_year,
                    movieGenre: data.movie_genre, 
                    movieRating: data.imdb_rating
                })
            });

            setPopularMovies([...popularMovies, ...newArray])

        }));
    }, [render]);

至于你的按钮没有重新呈现你想要的数据,我不是 100% 确定,但这可能与 JavaScript 处理承诺的方式有关。您 useEffect 中的 popularMoviesRef.once 调用可能与您的 db.collection('users').doc(user_id).update 同时发生。您可以通过将 console.log() 添加到 useEffect 函数来测试它,以尝试查看它是否在 "Successfully Updated!".

之前记录

看这里: