Search code examples
angulartypescriptngrxmemoizationngrx-store

Is it possible to use MemoizedSelector as an input parameter for createSelector?


I'm trying to create a MemoizedSelector with createSelector. I want to use another MemoizedSelector as it's input. But after root state is changed this selector doesn't recalculate new selected value.

E.g. we have a state:

const initialState: RootState = {
  user: {
    name: {
      first: 'John',
      last: 'Doe',
    },
  },
};

and a chain of reducers:

const userSelector = (state: RootState) => state.user;

const nameSelector : MemoizedSelector<RootState, UserName> = createSelector(
  userSelector,
  (user: User) => user.name
);

const lastNameSelector : MemoizedSelector<RootState, string>  = createSelector(
  nameSelector,
  (userName: UserName) => userName.last
);

So when we select the first name and change it in a reducer:

this.store$.select(lastNameSelector).subscribe(it => console.log(it));
this.store$.dispatch({type:'CHANGE_LAST_NAME', payload: 'Brown'});

We can't see "Brown" in the log.

I can make it work with clean function:

const lastNameSelector = (state: RootState) => state.user.name.last;

but I want to take advantage of memoization, and don't recalculate user's last name when any other state param is changed. What can I do to make it work?

Example app: https://github.com/vadim-shb/ngrx-issue

UPDATE:

The issue was in the reducer:

export function userReducer(state: UserData = initialState, action: any) {
  if (action.type === 'CHANGE_LAST_NAME') {
    let result = {
      ...state
    };
    result.name.last = action.payload;
    return result;
  }
  return state;
}

Solution

  • in your reducer, try this:

    if (action.type === 'CHANGE_LAST_NAME') {
        let result = {
          ...state,
          name: {
            ...state.name,
            last: action.payload
          }
         }
        return result;
      }