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

如何通过NGXS内置API编程式选择嵌套属性更新状态

NGXS 动态属性嵌套状态更新方案

使用NGXS内置的setState、patch、updateItemAPI更新深层嵌套状态时,直接传入计算属性名[action.taskType]做动态属性更新会遇到类型校验问题,原有手动重建整个状态对象的写法冗余、可维护性差。
下面给出基于官方操作符的正确实现。

相关定义参考

  • 根状态模型
interface TaskStateModel {
  tasks: Tasks;
}
  • Tasks类
export class Tasks { // 属性名已做混淆处理
  property1: UniqueTask[]
  proprety2: UniqueTask[]
  // 其余属性省略...
  constructor(init?: Partial<Tasks>) {
    Object.assign(this, init);
  }
}
  • UniqueTask类
export class UniqueTask { // 仅展示相关属性
  taskIds: string[];
  asssignedUserName: string;

  constructor(init?: Partial<UniqueTask>) {
    Object.assign(this, init);
  }
}
  • AssignTask动作
export class AssignTask {
  static readonly type = '[Task] Assign Task';

  constructor(public taskIds: string[], public taskType: string) {}
}

原有冗余实现

旧方案需要手动遍历所有状态属性重建对象,代码冗余度高:

@Action(AssignTask)
assignTasks(ctx: StateContext<TaskStateModel>, action: AssignTask) {
  return this.taskService.assignTasks(action.taskIds).pipe(
    tap(response => {
      if (response.ok) {
        action.taskIds.forEach(taskId => {
          const state = ctx.getState();
          const newState: TaskStateModel = {tasks: new Tasks()};
          const taskIndexToBeAssigned = state.tasks[action.taskType].findIndex(task => task.taskIds.includes(taskId));
          Object.keys(state.tasks).forEach(property => {
            if (property !== action.taskType) {
              newState.tasks[property] = state.tasks[property];
            }
            else {
              newState.tasks[property] = state.tasks[property].map((task, index) => {
                const assignedUserName = (taskIndexToBeAssigned === index) ? this.currentUserService.currentUser.name : task.assignedUserName;
                return new UniqueTask({
                  ...task,
                  assignedUserName
                });
              });
            }
          });
          ctx.setState(newState);
        });
      }
    })
  );
}

正确实现方案

patch方法原生支持ES6计算属性名,之前的报错本质是TypeScript类型校验问题,同时原尝试代码存在数组方法调用错误、重复触发状态更新的问题,修正后代码如下:

import { patch, updateItem } from '@ngxs/store/operators';

@Action(AssignTask)
assignTasks(ctx: StateContext<TaskStateModel>, action: AssignTask) {
  return this.taskService.assignTasks(action.taskIds).pipe(
    tap(response => {
      if (!response.ok) return;
      const currentUserName = this.currentUserService.currentUser.name;
      
      // 构造动态属性更新配置
      const taskTypePatch = {
        [action.taskType]: updateItem<UniqueTask>(
          // 匹配所有包含目标taskId的任务项
          task => action.taskIds.some(taskId => task.taskIds.includes(taskId)),
          // 更新指派人字段
          task => ({ ...task, assignedUserName: currentUserName })
        )
      };

      ctx.setState(
        patch({
          tasks: patch(taskTypePatch as any)
        })
      );
    })
  );
}

优化点说明

  • 去掉了循环单个taskId重复调用setState的逻辑,一次性匹配所有需要更新的任务,仅触发一次状态变更,性能更好
  • 修正了原尝试代码的语法错误:task.taskIds(taskId)是错误的函数调用写法,taskIds为数组类型,需要用includes方法判断元素是否存在
  • 类型报错可通过两种方式解决:
    • 临时使用as any类型断言绕过校验
    • 从根源约束taskType类型,修改AssignTask类定义,将taskType的类型从string改为keyof Tasks,TS即可自动识别计算属性的合法性,无需类型断言:
      export class AssignTask {
        static readonly type = '[Task] Assign Task';
      
        constructor(
          public taskIds: string[], 
          public taskType: keyof Tasks
        ) {}
      }
      

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 11:12:32