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

如何在React状态中修改已初始化的类实例数组对象

如何高效修改React状态中类实例数组的属性并遵循最佳实践

场景说明

开发React应用时,使用useState存储一组Journey类实例作为状态,该类用于解析数据库数据。偶尔需要在状态初始化完成后修改这些实例的属性,但现有两种方案都存在问题:

初始代码示例

import React, { useEffect, useState } from 'react';

const initialJourneysFromDB = [
    { idDB: 1, vehicleDB: "Car", dateDB: "2023-06-01", childrenDB: 2 , adultsDB: 1},
    { idDB: 2, vehicleDB: "Bus", dateDB: "2023-06-02", childrenDB: 3 , adultsDB: 3},
    { idDB: 1, vehicleDB: "Car", dateDB: "2023-06-05", childrenDB: 1 , adultsDB: 2},
    { idDB: 3, vehicleDB: "Jet", dateDB: "2023-06-03", childrenDB: 0 , adultsDB: 2},
];

// 该类结构复杂,核心作用是解析数据库数据
// - 包含上百个参数,通过对象点标记获取
// - 重命名数据库字段
// - 解析过程中会丢失部分原始数据
// - 部分属性需要初始化后基于全量实例计算得出
class Journey {
    constructor(params) {
        this.id = params.idDB;
        this.vehicle = params.vehicleDB;
        this.date = params.dateDB;
        this.passengers = params.childrenDB + params.adultsDB; // 丢失儿童和成人的单独数量
        this.pastJourneysForSameVehicle = null; // 初始化后计算的属性
    }

    setPastJourneys(journeys) {
        this.pastJourneysForSameVehicle = journeys.filter(
            journey => journey.vehicle === this.vehicle && journey.date < this.date
        );
    }
}

function App() {
    
    const [journeys, setJourneys] = useState([]);
    
    useEffect(() => {
        const parsedJourneyList = initialJourneysFromDB.map(journeyData => {
            return new Journey(journeyData);
        })
        parsedJourneyList.forEach(journeyData => {
            journeyData.setPastJourneys(parsedJourneyList); // 初始化后设置关联属性
        })
        setJourneys(parsedJourneyList)
    }, [])

    return (
        // ...JSX
    );
}

现有方案的问题

1. 直接修改对象

直接修改实例属性,违反React状态不可变原则,可能导致组件无法正确更新:

const modifyPropertyDirectly = () => {
    const updatedJourneys = [...journeys];
    const journeyToUpdate = updatedJourneys.find(journey => journey.id === 1)
    journeyToUpdate.date = "2023-06-04";
    setJourneys(updatedJourneys);
};

2. 重新创建普通对象

通过展开运算符复制属性创建新对象,会破坏原有的类实例结构,丢失类方法和内部逻辑:

const recreateObjectWithModifiedProperty = () => {
    const updatedJourneys = journeys.map(journey => {
        const newObj = {...journey}
        if (journey.id === 1) {
            newObj.date = "2023-06-04"
        }
        return newObj;
    });
    setJourneys(updatedJourneys);
};

符合最佳实践的解决方案

方案一:给Journey类添加不可变更新方法

在类中新增一个方法,返回包含修改属性的新类实例,既保持类结构,又遵循不可变原则:

class Journey {
    // ... 原有构造函数和方法

    // 新增不可变更新方法
    update(updates) {
        // 创建包含当前实例所有属性的对象,合并更新内容
        const updatedParams = {
            idDB: this.id,
            vehicleDB: this.vehicle,
            dateDB: this.date,
            // 注意:如果原始数据库字段已丢失,需要确保这里能构造出正确的参数
            childrenDB: 0, // 若原始数据丢失,需根据现有字段反推或提前存储
            adultsDB: this.passengers
        };
        Object.assign(updatedParams, updates);
        const newJourney = new Journey(updatedParams);
        return newJourney;
    }
}

// 使用方式
const updateJourneyImmutably = () => {
    setJourneys(prevJourneys => {
        const updatedList = prevJourneys.map(journey => {
            if (journey.id === 1) {
                // 返回新实例,传入需要修改的数据库字段
                return journey.update({ dateDB: "2023-06-04" });
            }
            return journey;
        });
        // 重新计算所有实例的关联属性
        updatedList.forEach(journey => journey.setPastJourneys(updatedList));
        return updatedList;
    });
};

方案二:使用复制构造函数

给Journey类添加支持从现有实例创建新实例的构造逻辑,直接基于旧实例生成新实例并修改属性:

class Journey {
    constructor(params) {
        // 如果传入的是Journey实例,直接复制属性
        if (params instanceof Journey) {
            this.id = params.id;
            this.vehicle = params.vehicle;
            this.date = params.date;
            this.passengers = params.passengers;
            this.pastJourneysForSameVehicle = params.pastJourneysForSameVehicle;
        } else {
            // 原有数据库参数解析逻辑
            this.id = params.idDB;
            this.vehicle = params.vehicleDB;
            this.date = params.dateDB;
            this.passengers = params.childrenDB + params.adultsDB;
            this.pastJourneysForSameVehicle = null;
        }
        // 支持传入修改的属性
        if (params.updates) {
            Object.assign(this, params.updates);
        }
    }

    // ... 原有setPastJourneys方法
}

// 使用方式
const updateWithCopyConstructor = () => {
    setJourneys(prevJourneys => {
        const updatedList = prevJourneys.map(journey => {
            if (journey.id === 1) {
                // 创建新实例,传入旧实例和要修改的属性
                return new Journey({
                    ...journey,
                    updates: { date: "2023-06-04" }
                });
            }
            return journey;
        });
        // 重新计算关联属性
        updatedList.forEach(journey => journey.setPastJourneys(updatedList));
        return updatedList;
    });
};

方案三:基于现有实例属性重新实例化

如果类的构造逻辑允许,直接提取现有实例的属性,合并修改后创建新实例:

const updateByReinstantiating = () => {
    setJourneys(prevJourneys => {
        const updatedList = prevJourneys.map(journey => {
            if (journey.id === 1) {
                // 提取当前实例的属性,构造符合构造函数要求的参数
                const newParams = {
                    idDB: journey.id,
                    vehicleDB: journey.vehicle,
                    dateDB: "2023-06-04", // 修改目标属性
                    childrenDB: 0, // 若原始数据丢失,需根据现有字段反推或提前存储
                    adultsDB: journey.passengers
                };
                return new Journey(newParams);
            }
            return journey;
        });
        // 重新计算依赖全量数据的属性
        updatedList.forEach(journey => journey.setPastJourneys(updatedList));
        return updatedList;
    });
};

核心原则总结

  • 始终遵循React状态不可变原则,绝不直接修改状态中的实例
  • 保持Journey类的实例结构,确保类的方法和内部逻辑可用
  • 修改后需重新计算依赖全量数据的属性,保证数据一致性

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 11:52:28