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

NgRx Reducer问题:修改数组中指定University对象的状态

问题解决:NgRx中修改指定University的total_students值

核心问题梳理

你的代码存在几个关键问题:

  • university.reducer.ts 中定义了 UniversityState 接口,但初始状态却使用了 Array<University>,类型不匹配
  • 修改学生总数的逻辑放错了位置,应该在维护大学列表的 universityReducer 中处理,而非 collectionReducer(该reducer是官方书籍教程遗留的,用于维护收藏ID列表,和当前修改学生总数的需求无关)
  • 状态更新逻辑错误,未遵循NgRx不可变更新的原则

修正后的代码实现

1. 修正 Reducers/university.reducer.ts

统一状态结构,并添加修改学生总数的reducer逻辑:

import { createReducer, on } from '@ngrx/store';
import { UniversityActions, UniversityApiActions } from '@actions/actions/university.actions';
import { University } from '@models/university.model';

export interface UniversityState {
  universities: University[];
  status: 'start' | 'loading' | 'error' | 'success';
  error: string | null;
}

// 初始状态严格匹配接口结构
export const initialState: UniversityState = {
  universities: [],
  status: 'start',
  error: null
};

export const universityReducer = createReducer(
  initialState,
  // 处理获取大学列表的动作
  on(UniversityApiActions.retrievedUniversities, (state, { universities }) => ({
    ...state,
    universities,
    status: 'success'
  })),
  // 处理修改学生总数的动作
  on(UniversityActions.changeStudentTotal, (state, { universityId, total_students }) => ({
    ...state,
    // 不可变更新:遍历数组,找到目标ID的大学后创建新对象
    universities: state.universities.map(university => {
      if (university.id === universityId) {
        // 复制原属性并更新total_students
        return { ...university, total_students };
      }
      // 其他大学直接返回原对象
      return university;
    })
  }))
);

2. 调整 Reducers/collection.reducer.ts(可选)

如果你的需求不需要维护收藏的大学ID列表,可以直接删除这个reducer;如果需要保留,注意该reducer的状态是string[],和修改学生总数的动作无关,无需在其中处理changeStudentTotal。

3. 修正 Selectors/university.selectors.ts

由于状态结构改为UniversityState,调整选择器以匹配新结构:

import { createSelector, createFeatureSelector } from '@ngrx/store';
import { University } from '@models/university.model';
import { UniversityState } from '@state/reducers/university.reducer';

// 获取整个UniversityState
export const selectUniversityState = createFeatureSelector<UniversityState>('universities');

// 获取所有大学列表
export const selectAllUniversities = createSelector(
  selectUniversityState,
  (state) => state.universities
);

// 根据ID获取单个大学(可选)
export const selectUniversityById = (universityId: string) => createSelector(
  selectAllUniversities,
  (universities) => universities.find(u => u.id === universityId)
);

// 原收藏列表选择器(保留collectionReducer时使用)
export const selectCollectionState = createFeatureSelector<Array<string>>('collection');

export const selectCollectionUniversities = createSelector(
  selectAllUniversities,
  selectCollectionState,
  (universities, collection) => {
    return collection.map(id => universities.find(u => u.id === id)!);
  }
);

4. 动作部分无需修改

你定义的university.actions.ts是正确的,changeStudentTotal动作携带的universityId和total_students参数完全匹配需求。


关键逻辑说明

NgRx要求状态更新必须是不可变的,所以不能直接修改原数组或对象:

  1. 使用扩展运算符...复制原状态对象,保留未修改的属性
  2. 使用map遍历大学数组,找到目标ID的大学时,创建新对象(复制原属性+更新total_students),其他对象直接返回
  3. 既保证了状态的不可变性,又正确完成了指定大学的学生总数更新

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 13:01:20