-1

I'm trying out NgRx (redux) in Angular and the ...state in the example below, I can not fully understand. I searched for it and generally understood it as spread, but not sure why the data attributes in the Interface State are repeated in the return{} block of the reducer's switch, as the ... would spread them anyway? Can anyone help me understand this please?

export interface State {
  sessionData: Map<string, string>;
  requesting: boolean;
  hasError: boolean;
  status: StatusModel;
}

export function sessionReducer(state: State = INITIAL_STATE, action: Session.Actions): State {
      switch (action.type) {
        case Session.REQUEST_SESSION_DATA:
          return {
            ...state,
            requesting: true,
            hasError: false,
            status: undefined,
          };
      }
}

PS: I've looked at the thread here and generally get that spread does exactly that, spread out. But here in the context of Redux/NgRx, trying to understand why the return{} has ...state and the three additional properties.

danday74
  • 52,471
  • 49
  • 232
  • 283
Aragorn
  • 5,021
  • 5
  • 26
  • 37
  • Sounds like it should only be including `sessionData`, and not `...state`, assuming the `State` will only contain those properties and no others – CertainPerformance Oct 01 '18 at 19:56

4 Answers4

2

The point of the state is that it's inmutable (a new object is returned, instead a modified one). So, if you want to modify the state adding new values to it you need to return the current state plus the new values yo want to add to the previous state. In that example with the spread operator ..., you are returning a new inmutable object containing the previous state plus three new properties requesting, hasError and status. You can think of doing this:

export function sessionReducer(state: State = INITIAL_STATE, action: Session.Actions): State {
      switch (action.type) {
        case Session.REQUEST_SESSION_DATA:
          state.requesting = true;
          state.hasError: false;
          state.status: undefined;

          return state;
      }
}

But you cannot do this, because you are breaking the philosophy of the state, new inmutable objects instead of modified ones :)

In your example, we need to know how the INITIAL_STATE is initialized, but I think that it only contains the sessionData property. So, in that example you are returning the sessionData plus the remaining properties.

In the link below you can see that the spread operator is a common used operator in the Redux world to return the current state as a new object (it's a Redux for React example, but it works exactly the same in Angular).

In Angular it's a very common pattern using the Redux pattern with the OnPush change detection strategy, because you are telling Angular to check only for reference changes in the component @Input instead of compare objects comparing property by property. That's a big advantage in performance.

Use of Spread Operator with Redux

Elias Garcia
  • 6,772
  • 11
  • 34
  • 62
1

... is called spread operator.

Spread operator "unpacks" everything from that object.

return {
    ...state,
    requesting: true,
    hasError: false,
    status: undefined,
};

is the same as

return {
    sessionData: state.sessionData,
    requesting: state.requesting,
    hasError: state.hasError
    status: state.status,
    requesting: true,
    hasError: false,
    status: undefined,
};
PlayMa256
  • 6,603
  • 2
  • 34
  • 54
0

You are correct that this is the spread operator. Basically, it extracts all of the properties on a given object. Another way of writing this would be:

let newState = {
  sessionData: state.sessionData,
  requesting: state.requesting,
  hasError: state.hasError,
  status: state.status
};
newState.requesting = true;
newState.hasError = true;
newState.status = undefined;
return newState;

The spread operator saves you the effort of knowing what all of the properties are called and ensures that all of the values that you don't change get carried over as is.

Put another way

{ ...state }

makes a shallow copy of state. You're then free to modify that shallow copy any way you want.

Mike Cluck
  • 31,869
  • 13
  • 80
  • 91
  • It seems pretty pointless to spread an object when you only actually want one property from that object though, I would think? (I don't quite understand why the original code is using spread) – CertainPerformance Oct 01 '18 at 20:03
  • @CertainPerformance exactly my confusion. Therefore I thought I misunderstood or had an understanding gap on ...| – Aragorn Oct 01 '18 at 20:12
  • @Vivek It protects you from updates to the shape of the state. If you decide to add more properties to the state later, you don't have to remember to update everywhere that you copied specific properties. It also results in less typing. – Mike Cluck Oct 02 '18 at 13:13
0

This is saying return a new object with all the properties of state and add / amend the 3 additional properties.

Worth knowing that {...state} deeply equals state such that:

_.isEqual(state, {...state}) // returns true

but:

{...state} !== state

This is because {} creates a new object and the spread operator just adds props to that object. Therefore, the object reference of state is not the same as the new object reference. This is how change detection often works. It looks to see if the object reference has changed. If it has it knows there is a change and acts accordingly. The reason this approach is used is because it is much faster than checking for deep equality whereby you have to check ALL the properties.

Reducers often use the spread operator where they only want to modify one of the properties on the state. If they did this:

state.requesting = true
return state

Then in this example, the object reference has not changed, therefore change detection does not kick in and you are stuffed.

To enforce change detection there is a great little lib called ngrx-store-freeze which prevents you from accidentally mutating state which can cause many hard to reproduce bugs. This lib is actually recommended by Udemy on their paid for ngrx course.

danday74
  • 52,471
  • 49
  • 232
  • 283