2
votes

Can we Memoization a object from the ngrx store? I mean, I can memoization a simple primitive value but lets say I want to memoization an object/array.

**REDCUER**

export const initialState: AuthState = {
  isAutenticated: false,
  msg: ""
};

const reducer = createReducer(
  initialState,
  on(isAuths, (state: AuthState, action) =>({ ...state })),
  on(isAuthChanged, (state: AuthState, action) => {
    return {
      ...state,
      isAutenticated: action.isAutenticated
    }
  })

);


**SELECTORS**

export const selectAuth = (state: AppState) => state.auth
export const selectAuthState = createSelector(
    selectAuth,
    (state: AuthState) => state <------------ NO MEMORIZATION
);


export interface AuthState {
  isAutenticated: boolean;
  msg: string;
}

for example INITAL STATE: {isAutenticated: false, msg: ""}

And:

  this.store.select(selectAuthState)
   .subscribe(r=>console.log(r))
       
setTimeout(()=>
        this.store.dispatch(isAuthChanged({ isAutenticated: false, msg: "" }))
    ,5000)

so this will be trigger First time initial value, and then with the new event after 5 sec even if its the same values {isAutenticated: false, msg: ""}. I want it to be trigger only once.

if i will do something like this:

   export const selectAuthState = createSelector(
        selectAuth,
        (state: AuthState) => state.isAutenticated <------------ MEMORIZATION only on the state isAutenticated
    );

So this will be works only on the state isAutenticated, and the subscribe function will return true or false, but i want to return the full object.

I could check for the same values in the reducer and check if they equal and then return the same state.

like this:

  on(isAuthChanged, (state: AuthState, action) => {
    if (state.isAutenticated == action.isAutenticated && state.msg == action.msg) {
      return state;
    } else {
      return {
        ...state,
        isAutenticated: action.isAutenticated
      }
    }
  })

But I wonder if there another way to achieve this approach using selectors to memorization the whole Object?

thanks

1

1 Answers

2
votes

Ngrx's select function relies on distinctUntilChanged behind the scenes. Hence, that is why returning a simple boolean (or any primitive) will work, but returning an object will trigger the selector. Remember, distinctUntilChanged will filter based on a reference equality check (i.e. object-a === object-b).

In order to achieve the memoization behavior that you want for a certain object state, you have to focus on the reducer. For example, in the reducer function for isAuthChanged, you can check whether the state object has changed by doing a deep equality check: if the object changed, return a new state, otherwise, return the same state.

For example:

 on(isAuthChanged, (state: AuthState, action) => {
    if (deepEqual(state, action)) {
       return state;
    } else {
       return {
        ...state,
        isAutenticated: action.isAutenticated
      }
    }
  })

...

function deepEqual(a, b) {
   return a.isAutenticated == a.isAutenticated && b.msg == b.msg
}

This is really no different than what you've already posted, so this answer just confirms that you're on the right track.

You can optimize this a bit, by leveraging fast-deep-equal.

For example:

  on(isAuthChanged, (state: AuthState, action) => {
    return equal(state, action) ? 
       state : 
       { ...state, isAutenticated: action.isAutenticated};
  })