Angular NGXS 不会重新计算选择器,除非所选数据的状态已更新

Angular NGXS don't recalculate selectors unless the selected data is updated in state

我的 NGXS 状态定义如下

export interface Project{
  _id:string,
  name:string
}

export class ProjectStateModel {
    projects: Project[];
}

@State<ProjectStateModel>({
    name: 'Projects',
    defaults: {
        projects: [{_id:'111', name:'Project 1'}, {_id:'222', name: 'Project 2'}]
    }
})
@Selector()
    static getAll(state: ProjectStateModel) {
        return state.projects;
    }

    @Selector()
    static getById(state: ProjectStateModel) {
        return (id) => {
            return state.projects.find(p => p._id === id);
        };
    }

    @Action(ProjectsAction.Add)
    add({ getState, patchState }: StateContext<ProjectStateModel>, { payload }: ProjectsAction.Add) 
    {
        const state = getState();
        patchState({
            projects: [...state.projects, ...payload]
        })
    }

这是一个非常直接的状态实现。国家默认有2个项目; 2 个选择器,一个用于获取所有项目,另一个用于通过 ID 和一个添加新项目的操作。

我正在使用其中一个组件中的状态,如下所示

export class ProjectDetailComponent{
    selectedPROJ: Project;
    selectedPROJ$: Observable<Project>;
    
    constructor(){
        this.selectedPROJ$ = this.store.select(ProjectState.getById)
                .pipe(map(filterFn => filterFn('111')));
        this.test = this.selectedPROJ$.subscribe(p => {
          console.log(p); //this get logged every time there is a change in state even though the project 111 hasn't changed
        })
    }
}

我面临的问题是每次我更新状态(比如向状态添加新项目)时,即使从状态中选择的项目没有改变,通过 id 获取的选择器也会重新触发。

例如,在上面显示的代码中,我选择了 ID 为 111 的项目。如果我发送 Add 操作来添加新项目,选择器 getById 会重新触发。

在 NGSX 中有没有什么方法可以定义一个参数化的选择器,只有当所选项目的状态发生变化时才会被触发?

我看过 NGXS 文档中的 injectContainerState,但不明白如何将其与此参数化选择器一起使用。

选择器将根据其选择器被记忆。

@Selector() // <-- no selector injected 
static getById(state: ProjectStateModel)

由于上面的选择器没有任何对状态标记、状态 class 或其他选择器的引用,因此默认情况下,每次状态更改时选择器都会 运行。选择器的强大之处在于您可以在它们的基础上创建细粒度的选择器。

另一种选择是使用动态选择器。基本上在需要时创建选择器并传入所需的参数。这是一个例子:

https://stackblitz.com/edit/ngxs-selectors-upgky4?file=src/app/app.component.ts

该示例没有展示最佳做法,而且非常粗糙。例如,您不能更改 ID。但它确实向您展示了另一种实现您想要的方法。

很棒的资源:https://www.youtube.com/watch?v=y3F99IsnNvI