首页 > 解决方案 > 对象数组在 React 中未完全渲染,并在控制台上显示与实际不匹配的数组长度

问题描述

我在 React 中渲染一组对象时遇到问题。出于某种原因,在它拥有的 5 个元素中,它只呈现 3 个。我在控制台中检查了它,以下显示:

在此处输入图像描述

出于某种原因,最初在控制台上显示该数组有 3 个元素,但如果我在控制台中检查它,则有 5 个元素而不是 3 个。准确地说,呈现的元素是前 3 个。这是 React 组件,以防万一它可能会有所帮助:

import React, { useEffect, useState } from 'react';
import { Link } from 'react-router-dom';

import './RegisterBlock.css';

const RegisterBlock = () => {

    const [gamertag, setGamerTag] = useState("");
    const [password, setPassword] = useState("");
    const [repassword, setRepassword] = useState("");
    const [mail, setMail] = useState("");
    const [isChecked, setIsChecked] = useState(false);
    const [errors, setErrors] = useState(null);

    const handleChange = (event) => {
        event.target.name === "password" ? setPassword(event.target.value) : 
            event.target.name === "repassword" ? setRepassword(event.target.value) : 
            event.target.name === "gamertag" ? setGamerTag(event.target.value) : 
            event.target.name === "email" ? setMail(event.target.value) : setIsChecked(!isChecked) ;
    }

    const handleSubmit = (event) => {
        event.preventDefault();

        var totalErrors = [];

        if (gamertag.length === 0 || gamertag.length > 30) {
            totalErrors.push({"error": "La contraseña debe tener entre 2 y 30 caracteres"});
        }

        if (!/^[a-zA-Z0-9]+@[a-zA-Z0-9]+\.[A-Za-z]+$/.test(mail)) {
            totalErrors.push({"error": "Introduce un correo válido"});
        }

        if (!isChecked) {
            totalErrors.push({"error": "Debes aceptar haber leído las políticas para poder registrarte"});
        }

        const url = "URL API" + mail;
        fetch(url)
            .then(response => response.json())
            .then(data => {
                data.length > 0 && totalErrors.push({"error": "Ya hay una cuenta con este correo"});
            });

        const urlGamertag = "URL API" + gamertag;
        fetch(urlGamertag)
            .then(response => response.json())
            .then(data => {
                data.length > 0 && totalErrors.push({"error": "El gamertag ya ha sido usado por otro usuario"});
            });

        setErrors(totalErrors);
    }

    useEffect(() => {
        if (errors?.length === 0) {
            const requestOptions = {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({ 
                    name: gamertag,
                    password: password,
                    email: mail
                })
            };
            fetch('URL API', requestOptions)
                .then(response => response.json())
        }  
    }, [errors?.length === 0])

    return (
        <div className="login-block">
            <div className="wrapper">
                <form onSubmit={handleSubmit} method="post">
                    <input type="text" placeholder="Gamertag" name="gamertag" value={gamertag} onChange={handleChange}/>
                    <input type="mail" placeholder="Email" name="email" value={mail} onChange={handleChange}/>
                    <input type="password" placeholder="Contraseña" name="password" value={password} onChange={handleChange}/>
                    <input type="password" placeholder="Verifica contraseña" name="repassword" value={repassword} onChange={handleChange}/>
                    <div className="form__checkbox">
                        <input onChange={handleChange} name="policy" id="policy" type="checkbox" checked={isChecked} />
                        <label htmlFor="policy">Al registrarte confirmas haber leído la <Link to="/politica-de-privacidad">Política de privacidad</Link> y Aviso legal.</label>
                    </div>
                    {console.log(errors)}
                    {
                        errors?.length > 0 && (
                            <ul className="form--errors">
                                {
                                    errors.map((item, index) => {
                                        return <li key={index}>{item.error}</li>
                                    })
                                }
                            </ul>
                        )
                    }
                    <input type="submit" value="Registrarse"/>
                </form>
            </div>
        </div>  
    )
}

export default RegisterBlock;

标签: javascriptreactjsobject

解决方案


您的前 3 个验证是同步的,因此它们会立即添加到数组中并呈现。然而,最后 2 个在服务器端进行验证,这需要一些时间。当响应到达时,渲染过程已经完成。尽管错误被添加到数组中,但对数组本身的引用保持不变,因此 React 不会接收更改。所以这里有 2 个选项:setErrors在每个响应上使用克隆数组,或者在渲染任何内容之前等待所有验证完成。第一个会导致冲洗,所以在这种情况下最好等待fetch. 请注意,您有 2 个请求,可能可以同时发出,因此可能值得将它们包装成Promise.all. 顺序选项(一次一个验证):

await fetch(url)
...
await fetch(urlGamertag)
setErrors(totalErrors);

同时触发两个请求:

const firstPromise = fetch(url)
...
const secondPromise =fetch(urlGamertag)
...
await Promise.all([firstPromice , secondPromise ])
setErrors(totalErrors);

推荐阅读