NGRX 减速器的多个实例的选择器
Selectors for multiple instance of NGRX reducers
我有一个用于搜索的减速器,我意识到它需要用于多个不相关的搜索组件。因此,通过查看 Redux 文档,我发现了高阶 reducers (http://redux.js.org/docs/recipes/reducers/ReusingReducerLogic.html#customizing-behavior-with-higher-order-reducers) (meta reducers in ngrx) and used that to create 2 'instances' of my search reducer. I then found in the same documentation that this will appear to work with selectors but actually has an issue with the memoization (http://redux.js.org/docs/recipes/ComputingDerivedData.html#accessing-react-props-in-selectors) 的概念。那篇文章引用了一个名为 'mapStateToProps' 的函数,它似乎是 React 将商店数据连接到组件的特定方式(如果我理解正确的话......)。
ngrx 中是否有等效项,或者是否有另一种创建这些 selector 以与减速器的不同实例一起工作的方法?
下面是一个基于 ngrx 示例应用程序的轻微人为设计的示例:
reducers/searchReducer.ts:
export interface State {
ids: string[];
loading: boolean;
query: string;
};
const initialState: State = {
ids: [],
loading: false,
query: ''
};
export const createSearchReducer = (instanceName: string) => {
return (state = initialState, action: actions.Actions): State => {
const {name} = action; // Use this name to differentiate instances when dispatching an action.
if(name !== instanceName) return state;
switch (action.type) {
//...
}
}
}
reducers/index.ts:
export interface State {
search: fromSearch.State;
}
const reducers = {
search: combineReducers({
books: searchReducer.createReducer('books'),
magazines: searchReducer.createReducer('magazines')
}),
}
export const getSearchState = (state: State) => state.search;
// (1)
export const getSearchIds = createSelector(getSearchState, fromSearch.getIds);
我相信 getSearchIds select 或更高版本需要能够以某种方式指定它正在访问的搜索 Reducer 实例。 (奇怪的是,在我的代码中,它 似乎 可以工作,但我不确定它如何知道 select 来自哪个,我认为它有 Redux 文档中讨论的记忆问题) .
我会建议重新考虑你这样做的方式并使用相同的减速器并制作另一个开关盒。
与此无关,较新版本的 AOT 不喜欢使用“=>”来创建减速器。而是使用
export function SearchReducer (state : State = initialState, { type, payload }){
switch (type) {
//cases...
}
}
而且你不必使用 combineReducers,你可以直接构建你的 reducer 对象
let reducers = {
search: SearchReducer
}
说你的状态是接口状态类型可以让你利用这种类型。
虽然 Kevin 的回答对于我给出的人为设计的代码示例是有意义的,但如果每个减速器 'instance' 有很多属性或者如果您需要很多 'instances',则肯定存在维护问题。在这些情况下,您会在单个 reducer 上得到许多类似重复的属性(例如 'bookIds'、'magazineIds'、'dvdIds'、'microficheIds' 等)。
考虑到这一点,我回到了 Redux 文档并按照它找到了选择器的常见问题解答,特别是 How Do I create a Selector That Takes an Argument。
根据这些信息,我将其放在一起:
reducers/index.ts:
export const getBookSearchState = (state: State) => state.search;
export const getMagazineSearchState = (state: State) => state.search;
// A function to allow the developer to choose the instance of search reducer to target in their selector.
export const chooseSearchInstance = (instance: string): ((state: State) => searchReducer.State) => {
switch(instance) {
case 'books': {
return getBookSearchState;
}
case 'magazines': {
return getMagazineSearchState;
}
}
}
// Determines the instance based on the param and returns the selector function.
export const getSearchIds = (instance: string) => {
const searchState = chooseSearchInstance(instance);
return createSelector(searchState, state => state.ids);
}
在您知道要使用的减速器的某些组件中:
//...
class SearchComponent {
@Input()
searchType: string = 'books';
ids: Observable<number>;
constructor(private store: Store<fromRoot.State>) {
this.store.select(fromRoot.getSearchIds(searchType));
}
}
我有一个用于搜索的减速器,我意识到它需要用于多个不相关的搜索组件。因此,通过查看 Redux 文档,我发现了高阶 reducers (http://redux.js.org/docs/recipes/reducers/ReusingReducerLogic.html#customizing-behavior-with-higher-order-reducers) (meta reducers in ngrx) and used that to create 2 'instances' of my search reducer. I then found in the same documentation that this will appear to work with selectors but actually has an issue with the memoization (http://redux.js.org/docs/recipes/ComputingDerivedData.html#accessing-react-props-in-selectors) 的概念。那篇文章引用了一个名为 'mapStateToProps' 的函数,它似乎是 React 将商店数据连接到组件的特定方式(如果我理解正确的话......)。
ngrx 中是否有等效项,或者是否有另一种创建这些 selector 以与减速器的不同实例一起工作的方法?
下面是一个基于 ngrx 示例应用程序的轻微人为设计的示例:
reducers/searchReducer.ts:
export interface State {
ids: string[];
loading: boolean;
query: string;
};
const initialState: State = {
ids: [],
loading: false,
query: ''
};
export const createSearchReducer = (instanceName: string) => {
return (state = initialState, action: actions.Actions): State => {
const {name} = action; // Use this name to differentiate instances when dispatching an action.
if(name !== instanceName) return state;
switch (action.type) {
//...
}
}
}
reducers/index.ts:
export interface State {
search: fromSearch.State;
}
const reducers = {
search: combineReducers({
books: searchReducer.createReducer('books'),
magazines: searchReducer.createReducer('magazines')
}),
}
export const getSearchState = (state: State) => state.search;
// (1)
export const getSearchIds = createSelector(getSearchState, fromSearch.getIds);
我相信 getSearchIds select 或更高版本需要能够以某种方式指定它正在访问的搜索 Reducer 实例。 (奇怪的是,在我的代码中,它 似乎 可以工作,但我不确定它如何知道 select 来自哪个,我认为它有 Redux 文档中讨论的记忆问题) .
我会建议重新考虑你这样做的方式并使用相同的减速器并制作另一个开关盒。
与此无关,较新版本的 AOT 不喜欢使用“=>”来创建减速器。而是使用
export function SearchReducer (state : State = initialState, { type, payload }){
switch (type) {
//cases...
}
}
而且你不必使用 combineReducers,你可以直接构建你的 reducer 对象
let reducers = {
search: SearchReducer
}
说你的状态是接口状态类型可以让你利用这种类型。
虽然 Kevin 的回答对于我给出的人为设计的代码示例是有意义的,但如果每个减速器 'instance' 有很多属性或者如果您需要很多 'instances',则肯定存在维护问题。在这些情况下,您会在单个 reducer 上得到许多类似重复的属性(例如 'bookIds'、'magazineIds'、'dvdIds'、'microficheIds' 等)。
考虑到这一点,我回到了 Redux 文档并按照它找到了选择器的常见问题解答,特别是 How Do I create a Selector That Takes an Argument。
根据这些信息,我将其放在一起:
reducers/index.ts:
export const getBookSearchState = (state: State) => state.search;
export const getMagazineSearchState = (state: State) => state.search;
// A function to allow the developer to choose the instance of search reducer to target in their selector.
export const chooseSearchInstance = (instance: string): ((state: State) => searchReducer.State) => {
switch(instance) {
case 'books': {
return getBookSearchState;
}
case 'magazines': {
return getMagazineSearchState;
}
}
}
// Determines the instance based on the param and returns the selector function.
export const getSearchIds = (instance: string) => {
const searchState = chooseSearchInstance(instance);
return createSelector(searchState, state => state.ids);
}
在您知道要使用的减速器的某些组件中:
//...
class SearchComponent {
@Input()
searchType: string = 'books';
ids: Observable<number>;
constructor(private store: Store<fromRoot.State>) {
this.store.select(fromRoot.getSearchIds(searchType));
}
}