通过编程选择嵌套属性来突变状态

发布于 2025-02-08 22:10:59 字数 3209 浏览 4 评论 0 原文

我正在努力使用NGXS的内部API来突变我州的深层嵌套属性。

由于NGXS的状态是不可变的,因此我当前的解决方案是重新创建状态实例,然后将其与我旧状态的内容进行补充,并具有我想要突变的一个属性的条件。

我的状态模型:

interface TaskStateModel {
  tasks: Tasks;
}

任务类:

export class Tasks { // I have obfuscated the property names
property1: UniqueTask[]
proprety2: UniqueTask[]
// etc...
  constructor(init?: Partial<Tasks>) {
    Object.assign(this, init);
  }
}

iniquetask class:

export class UniqueTask { // Only including the relevant properties
taskIds: string[];
asssignedUserName: string;

  constructor(init?: Partial<UniqueTask>) {
    Object.assign(this, init);
  }
}

aistrizattask action> Action:

  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);
          });
        }
      })
    );
  }

Action aindisttask < /code>发送 taskType 以选择所需的属性和 taskid ,以选择要突变属性的数组中的元素。

我的目标是使用 setState patch updateItem 方法实现相同的结果。

对于它的价值,这是我自己自己做的尝试:

  @Action(AssignTask)
  assignTasks(ctx: StateContext<TaskStateModel>, action: AssignTask) {
    return this.taskService.assignTasks(action.taskIds).pipe(
      tap(response => {
        if (response.ok) {
          action.taskIds.forEach(taskId => {
            ctx.setState(patch({
              tasks: patch({
                [action.taskType] : updateItem<UniqueTask>(task => task.taskIds(taskId), // [action.taskType] cannot be used as a programmatic property selector here
                  task => {
                    return {...task, assignedUserName: this.currentUserService.currentUser.name};
                  })
              })
            }));
          });
        }
      })
    );
  }

I am struggling to use NGXS's internal API to mutate a deeply nested property in my state.

Since NGXS's states are immutable, my current (less than ideal) solution is to recreate a state instance, then hydrating it with the contents of my old state, with a condition about the one property I want mutated.

My state model :

interface TaskStateModel {
  tasks: Tasks;
}

The Tasks class :

export class Tasks { // I have obfuscated the property names
property1: UniqueTask[]
proprety2: UniqueTask[]
// etc...
  constructor(init?: Partial<Tasks>) {
    Object.assign(this, init);
  }
}

The UniqueTask class :

export class UniqueTask { // Only including the relevant properties
taskIds: string[];
asssignedUserName: string;

  constructor(init?: Partial<UniqueTask>) {
    Object.assign(this, init);
  }
}

The assignTask Action :

  export class AssignTask {
    static readonly type = '[Task] Assign Task';

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

  }

Here is my current solution :

  @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);
          });
        }
      })
    );
  }

The Action AssignTask sends a taskType to select the desired property, and a taskId, to select the element within the array whose property is to be mutated.

My objective is to achieve the same result with the setState, patch, and updateItem methods.

For what it's worth, here is my attempt to do it on my own :

  @Action(AssignTask)
  assignTasks(ctx: StateContext<TaskStateModel>, action: AssignTask) {
    return this.taskService.assignTasks(action.taskIds).pipe(
      tap(response => {
        if (response.ok) {
          action.taskIds.forEach(taskId => {
            ctx.setState(patch({
              tasks: patch({
                [action.taskType] : updateItem<UniqueTask>(task => task.taskIds(taskId), // [action.taskType] cannot be used as a programmatic property selector here
                  task => {
                    return {...task, assignedUserName: this.currentUserService.currentUser.name};
                  })
              })
            }));
          });
        }
      })
    );
  }

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

浅忆 2025-02-15 22:11:00

您可能想使用此NGXS实验室包
https://npmjs.com/package/ppackage/ppackage/@ngxs-labs-labs-labs-labs-labs-labs-labs-labs/immer-adapter

导入 immutableContext

import { ImmutableContext } from '@ngxs-labs/immer-adapter';

实现

export const AUTH_STATE_TOKEN = new StateToken<AuthStateModel>('auth');

export const authDefaults: AuthStateModel = {
  isAuthenticated: false,
  signInProvider: 0,
  profile: {
    loaded: false,
    displayName: '',
    photoURL: ''
  },
  request: {
    firebase: {
      loaded: false, 
      length: 0,
      errors: null, 
      data: null
    }
  }
};

@State<AuthStateModel>({
  name: AUTH_STATE_TOKEN,
  defaults: authDefaults
})

操作

  @Action(MyAction)
  @ImmutableContext()
  sampleFunction({ setState }: StateContext<AuthStateModel>, action: MyAction) {
    setState((state: MyStateModel) => {
      state.isAuthenticated = true;
      state.signInProvider = SignInProvider.SocialMedia;
      state.profile.loaded = true;
      state.profile.displayName = displayName;
      state.profile.photoURL = defaultImage(displayName);
      state.request.firebase.loaded = !!action.state;
      state.request.firebase.length = action.state ? 1 : 0;
      state.request.firebase.data = action.state;
      return state;
    });
  }

You might want to use this ngxs labs package
https://npmjs.com/package/@ngxs-labs/immer-adapter

Import ImmutableContext

import { ImmutableContext } from '@ngxs-labs/immer-adapter';

Implementation

export const AUTH_STATE_TOKEN = new StateToken<AuthStateModel>('auth');

export const authDefaults: AuthStateModel = {
  isAuthenticated: false,
  signInProvider: 0,
  profile: {
    loaded: false,
    displayName: '',
    photoURL: ''
  },
  request: {
    firebase: {
      loaded: false, 
      length: 0,
      errors: null, 
      data: null
    }
  }
};

@State<AuthStateModel>({
  name: AUTH_STATE_TOKEN,
  defaults: authDefaults
})

Action

  @Action(MyAction)
  @ImmutableContext()
  sampleFunction({ setState }: StateContext<AuthStateModel>, action: MyAction) {
    setState((state: MyStateModel) => {
      state.isAuthenticated = true;
      state.signInProvider = SignInProvider.SocialMedia;
      state.profile.loaded = true;
      state.profile.displayName = displayName;
      state.profile.photoURL = defaultImage(displayName);
      state.request.firebase.loaded = !!action.state;
      state.request.firebase.length = action.state ? 1 : 0;
      state.request.firebase.data = action.state;
      return state;
    });
  }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文