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

React复用CSV数据读取函数致表格数据混淆的问题解决请求

解决React组件CSV数据混淆与加载状态控制问题

问题背景

在React项目中,同一页面需渲染两个分别读取不同CSV文件的表格组件(BMS和BatterySpecs),二者复用GetData函数进行数据读取与解析。当前存在两个核心问题:

  • 数据混淆:BMS表格会显示电池规格表格的数据
  • 加载状态异常:无法在获取正确数据后立即终止BMS表格的加载状态

问题根源分析

原GetData函数中定义了全局变量values,所有调用该函数的组件实例会共享这个数组。当两个组件同时加载时,它们的CSV解析结果会互相覆盖,直接导致数据串扰。此外,加载状态的更新逻辑不够严谨,且缺少必要的副作用清理机制。

初步方案评估

你提出的修改方向是正确的——将全局values替换为组件内部的useState状态(data),避免了全局变量的共享问题。但方案仍存在几个需优化的细节:

  • 循环外定义的temp数组会累积所有CSV的数据,导致每个电池组的结果包含之前的数据
  • 加载状态在循环内判断i===batt-1时设置,逻辑不够健壮(若循环提前终止会导致加载状态无法更新)
  • 未清理from(Numbatt()).subscribe的订阅,可能引发内存泄漏
  • useEffect的依赖项不全,缺少props.file和props.slice,导致这两个参数变化时不会重新拉取数据

完善的解决方案

修改后的GetData函数

import React, { useState, useEffect, useRef } from "react";
import * as d3 from 'd3';
import { Numbatt } from './ReadBattInfo';
import { from } from 'rxjs';

function GetData(props) {
    const [data, setData] = useState([]);
    const [batt, setBatt] = useState(0);
    const [loading, setLoading] = useState(true);
    // 用于保存订阅实例,方便清理
    const subscriptionRef = useRef(null);

    useEffect(() => {
        // 订阅电池数量,保存订阅实例
        subscriptionRef.current = from(Numbatt()).subscribe(x => setBatt(x));
        
        // 清理函数:组件卸载或依赖变化时取消订阅
        return () => {
            if (subscriptionRef.current) {
                subscriptionRef.current.unsubscribe();
            }
        };
    }, []);

    useEffect(() => {
        // 当电池数量、文件路径或slice参数变化时重新加载
        if (batt === 0) {
            setLoading(false);
            return;
        }

        setLoading(true);
        const fetchData = async () => {
            try {
                const values = [];
                // 遍历每个电池组,串行请求CSV
                for (let i = 0; i < batt; i++) {
                    const csvfile = `./data/batt${i}/${props.file}`;
                    const csvData = await d3.csv(csvfile);
                    // 每个电池组单独处理temp,避免数据累积
                    const temp = csvData.map(d => d.value);
                    values[i] = temp.slice(props.slice);
                }
                // 所有请求完成后更新数据并结束加载状态
                setData(values);
                setLoading(false);
            } catch (err) {
                console.error('加载CSV数据失败:', err);
                setLoading(false);
            }
        };

        fetchData();
    }, [batt, props.file, props.slice]); // 添加完整的依赖项

    return { loading, data };
}

export default GetData

组件调用调整

在BMS和BatterySpecs组件中,将原values替换为修改后的data:

// BMS组件示例
function BMS() {
    const { loading, data } = GetData({ file: 'bmsfault.csv', slice: -6 });

    return (
        <>
            <div className="w3-main w3-panel" style={{marginLeft:"300px"}}>
                <div className="w3-row-padding" style={{margin: 0 -16+"px"}}>
                    <div>
                        <h5>BMS - Battery Managment System</h5>
                        {loading && <div>LOADING...</div>}
                        {!loading && data.map((value, i) => (
                            <table key={`bms${i}`} className="w3-table w3-border w3-white w3-half">
                                {/* 表格内容保持不变 */}
                            </table>
                        ))}
                    </div>
                </div>
            </div>
        </>
    );
}

关键优化点说明

  • 移除全局变量:将数据存储在组件内部的useState中,每个GetData实例拥有独立的数据状态,彻底解决串扰问题
  • 独立处理每个CSV的temp数组:在循环内部创建temp,避免不同电池组的数据互相污染
  • 完善加载状态控制:所有请求完成(或出错)后统一更新加载状态,确保状态正确切换
  • 清理RxJS订阅:使用useRef保存订阅实例,在组件卸载或依赖变化时取消订阅,避免内存泄漏
  • 完整的依赖项:将props.file和props.slice加入useEffect依赖,确保参数变化时重新加载数据

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 22:05:27