You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React如何单独计算JSON API返回的每个成绩数组总和

问题根源

你的计算逻辑存在4个核心错误,直接导致结果异常:

  • 异步state更新时序错误:gradePopulate循环中每次执行setAverage(listGrades)后,React的state更新不会同步生效,你紧接着调用Average.reduce()时拿到的是上一次渲染的旧state值,不是当前循环对应的学生成绩数组;而且循环过程中反复给同一个Average state赋值,最后只会保留最后一个学生的成绩数组,前面所有学生的成绩数据都会被覆盖。
  • 数据类型错误:接口返回的grades数组内元素是字符串格式,直接执行加法会触发字符串拼接,不是数值求和,就算拿到正确数组计算结果也会异常。
  • 逻辑绑定错误:所有学生的展开按钮都绑定了同一个gradePopulate方法,点击一次就会遍历全部25个学生执行计算,根本无法对应到你当前点击查看的单个学生;且渲染平均分的位置硬编码写死了9%,根本没有绑定计算结果。
  • 搜索逻辑错误:两个先后执行的if判断会覆盖按firstName过滤的结果,且判断时使用的是旧的input state值,不是输入框的最新值,过滤结果不准。
修复方案

不需要单独维护全局的平均分state,直接在数据处理阶段针对单个学生的成绩数组做计算即可,同时修正搜索、展开交互的逻辑:

import React, { useEffect, useState } from "react";
import Card from "@material-ui/core/Card";
import CardContent from '@material-ui/core/CardContent';
import Grid from "@material-ui/core/Grid";
import { Input } from "@material-ui/core";

function StudentProfiles()  {

    const [info, setInfo] = useState([]);
    const [input, setInput] = useState('');
    const [filtered, setFiltered] = useState([]);
    // 单独记录展开成绩的学生id,不用存全量成绩
    const [expandedId, setExpandedId] = useState(null);
    
    
    useEffect(() => {
         fetch("https://api.hatchways.io/assessment/students")
        .then(response => response.json())
        .then(json => {
            setInfo(json.students)
            setFiltered(json.students)
        })
    },[]);
    
    
    
    const filterChange = (e) => {
        const target = e.target.value;
        setInput(target);
        // 合并姓名判断,直接用最新输入值过滤
        const keyword = target.toLowerCase().trim();
        if(!keyword) {
            setFiltered(info);
            return
        }
        const result = info.filter((user) => {
            return user.firstName.toLowerCase().startsWith(keyword) || user.lastName.toLowerCase().startsWith(keyword); 
        });
        setFiltered(result);
    }
    // 单学生平均分计算工具方法
    const calcAverage = (grades) => {
        const total = grades.reduce((sum, score) => sum + Number(score), 0);
        return total / grades.length;
    }
    // 切换单个学生的展开状态
    const toggleExpand = (studentId) => {
        setExpandedId(prev => prev === studentId ? null : studentId)
    }

    return (
        <div>
            <Card className="card">
                <CardContent className="scrollbar scrollbar-primary mt-5 mx-auto">
                    <Input
                    className="searchBar"
                    icon="search"
                    placeholder="Search by name"
                    onChange={filterChange}
                    value={input}
                    />
                    {filtered.map((student) => (
                        <ul className = "border" key={student.id}>
                            <Grid item xs={3} sm={6} md={12} style={{display: "flex", gap:"3.5rem", paddingBottom:"8px"}}>
                                <img alt ="" src={student.pic} className="picture"></img> 
                                <Grid  container style={{display: "inline"}} align="left" justifyContent="flex-end" alignItems="flex-start">
                                    <Grid  className="studentNames">
                                    <span>{student.firstName + " " + student.lastName}</span>
                                    <Grid container align="right" justifyContent="flex-end" alignItems="flex-end" style={{display: "inline"}} >
                                        {/* 点击只切换当前学生的展开状态 */}
                                        <button  onClick={() => toggleExpand(student.id)} className="plus"></button></Grid>
                                    </Grid>
                                    <span>{student.email}</span>
                                    <br/>
                                    <span>{student.company}</span>
                                    <br/>
                                    <span>{student.skill}</span>
                                    <br/>
                                    {/* 直接渲染计算好的当前学生平均分 */}
                                    <span>Average: {calcAverage(student.grades)}%</span>
                                    {/* 展开时显示当前学生的所有成绩 */}
                                    {expandedId === student.id && (
                                        <div className="grade-list">
                                            {student.grades.map((score, idx) => (
                                                <div key={idx}>Test{idx+1}: {score}%</div>
                                            ))}
                                        </div>
                                    )}
                                </Grid>
                            </Grid>
                        </ul>
                ))}
                </CardContent>
            </Card>
        </div>
    ) 
}

export default StudentProfiles;
关键改动说明
  • 移除了多余的Average全局state,避免异步更新导致的取值错误,计算平均分时直接取当前学生的grades数组,转成数字后用reduce求和再算平均,结果完全和单个学生对应。
  • 用expandedId记录当前展开成绩的学生id,点击按钮只切换对应学生的展开状态,不会遍历全量学生做无效计算。
  • 修复了搜索过滤逻辑,合并firstName、lastName的判断条件,直接使用输入事件的最新值做过滤,不会出现结果覆盖的问题。
  • 接口返回数据后直接初始化filtered列表,解决首次加载时列表为空的问题。

内容的提问来源于stack exchange,提问作者stackoverflowrog

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.03 07:37:29