ngrx:如何将参数传递给createSelector方法中的选择器

use*_*127 12 ngrx angular

我的商店中有一个非常简单的状态:

const state = {
 records: [1,2,3],
};
Run Code Online (Sandbox Code Playgroud)

我有一个记录选择器:

export const getRecords = createSelector(getState, (state: State) => state.records));
Run Code Online (Sandbox Code Playgroud)

我现在想要的是具有单独的选择器,用于按索引获取每个记录。为此,我想以这种方式创建一个带有道具的通用选择器:

export const getRecordByIndex = createSelector(
getRecords,
(state: State, { index }) => state.records[index]),
);
Run Code Online (Sandbox Code Playgroud)

然后,创建几个特定的​​选择器,例如:

export const getFirstRecord = createSelector(
getRecordByIndex(/* somehow pass index = 0 to this selector */),
(firstRecord) => firstRecord),
);
Run Code Online (Sandbox Code Playgroud)

但是我没有提到如何在createSelector方法中使用参数将参数传递给带有选择器的选择器。可能吗?

Ian*_*son 11

我正在使用"@ngrx/entity": "7.2.0",并且可以看到道具被传递给每个选择器,例如在我正在调用的组件中:

this.isActive$ = this.store.pipe(select(fromClient.isActive, { id: 'someid' }));
Run Code Online (Sandbox Code Playgroud)

然后在我的减速器中,我有以下内容:

export const getState = createFeatureSelector('state');

export const getEntity = createSelector(
  getState,
  (state, props) => {
    // do something with props.id to get an entity then:
    return state;
  }
);

export const isActive: = createSelector(
  getEntity, // props are passed to here
  (state: any) => { // i don't add the props argument here, as i don't need them
    return state.isActive;
  }
);
Run Code Online (Sandbox Code Playgroud)


Mik*_*ple 11

这可以使用“工厂选择器”来完成,如 @timdeschryver 在他关于弃用propsfrom ngrx 选择器的评论中所述。

根据OP的请求,我将实现以下内容:

export const getRecordByIndex = (index: number) => createSelector(
   getRecords, (records) => records[index]
);
Run Code Online (Sandbox Code Playgroud)

然后可以将其用作:

const record$ = this.store.select(getRecordByIndex(1));
Run Code Online (Sandbox Code Playgroud)

这似乎是首选方式,因为“使用带有 props 的选择器”在 ngrx 文档中被标记为“已弃用”


Deb*_*ahK 7

从此博客文章中:https : //blog.angularindepth.com/ngrx-parameterized-selector-e3f610529f8

从NgRx 6.1开始,选择器还接受一个额外的props参数。这意味着您现在可以将选择器定义如下:

export const getCount = createSelector(
  getCounterValue, 
  (counter, props) => counter * props.multiply
);

this.counter = this.store.pipe(
  select(fromRoot.getCount, { multiply: 2 })
);
Run Code Online (Sandbox Code Playgroud)

嗯...但是重新阅读您的问题,您在问然后如何构建使用此选择器的另一个选择器?上面链接的文章建议构建工厂功能。

  • 我感到精力充沛,因为您分享了我的一个帖子!:)要回答这个问题:props属性会深入到选择器内的其他选择器。换句话说,`getRecordByIndex`还将检索props属性作为其最后一个参数。 (3认同)
  • props 已被弃用,我发现 @timdeschryver 的以下评论对于转换为工厂选择器非常有帮助:https://github.com/ngrx/platform/issues/2980#issuecomment-819551245 (3认同)
  • 该博客文章在媒体上不再存在。您可以在这里找到它:https://timdeschryver.dev/blog/parameterized-selectors (2认同)

小智 7

您可以使用投影仪功能:

export interface Record {
  // Some sort of record interface
}

export interface State {
  records: Record[];
}

export const getRecords = createSelector(
  getState,
  (state: State): Record[] => state.records)
);

export const getRecordByIndex = createSelector(
  getRecords,
  (records: Record[], { index }) => records[index]),
);

export const getFirstRecord = createSelector(
  getRecords,
  (records: Record[]) => getRecordByIndex.projector(records, { index: 0 })
);
Run Code Online (Sandbox Code Playgroud)

  • 这是实际问题的唯一答案,所有其他答案都没有抓住重点。该解决方案的缺点是“getFirstRecord”必须知道“getRecordByIndex”的输入选择器“getRecords”,才能将状态传递给投影仪函数。`getRecordByIndex` 的实现泄漏到了 `getFirstRecord` 中。 (3认同)

Ale*_*xei 7

我设法为选择器提供了参数,但对我的使用方式略有改变。例子:

选择器(我在这里不使用单独的功能)

export const selectReferentialsState = (state: AppState) => state.referentials;

export const referentialDataSelector = createSelector(
    selectReferentialsState,
    (state: ReferentialsState, props: { refType: Referential}) => state.data[props.refType]
);
Run Code Online (Sandbox Code Playgroud)

用法

this.availableRoles$ = this.store.select(state => referentialDataSelector(state, { refType: Referential.Role}));
Run Code Online (Sandbox Code Playgroud)

高级用法(带参数的级联选择器)

我将提供另一个示例来涵盖更复杂的场景,即必须定义依赖于需要参数 ( props)的选择器的选择器。这还包括更简单的用法语法 ( pipe+ select):

export const selectQuestionnaireTranslationInfo = createSelector(
    selectQuestionnaireTranslationState,
    (state: QuestionnaireTranslationState, props: { formId: number}) => state.entities[props.formId]
);

export const selectQuestionnaireLanguageProgress = createSelector(
    selectQuestionnaireTranslationInfo,
    (state: QuestionnaireTemplateTranslationFullInfo, props: {formId: number, langId: number }) =>
        state?.languageInfo?.find(li => li.spTranslationLanguageId === props.langId)
);

export const selectQuestionnaireLanguageProgressCount = createSelector(
    selectQuestionnaireLanguageProgress,
    (state: QuestionnaireTemplateTranslationLanguageInfo) =>
        state?.translatedResourceCount
);
Run Code Online (Sandbox Code Playgroud)

用法:

const props = { formId: this.templateId, langId: this.languageId};
this.progressCount$ = this.store.pipe(select(selectQuestionnaireLanguageProgressCount, props));`
Run Code Online (Sandbox Code Playgroud)

正如Ian Jamieson已经指出的,props 被合并并在选择器链中可用(这就是为什么最后一个选择器不需要显式声明 props,它们是“继承的”)。