0

I have this object below, I want to test that when i perform an action to change the state of the group key in the state that it updates correctly. As you can see there are way more key value pairs in this object than just group so I would like to spread the initialState object and write my test changing only the group value.

Here is the initialState:

export const initialState = {
  testing: false,
  filters: {
    start: moment()
      .startOf("month")
      .format(),
    end: moment()
      .endOf("month")
      .format(),
    city: "",
    group: "daily"
  },
  locations: []
};

My unit test:

test("It performs the setGroup action correctly", () => {
    const active = "month";

    const action = setGroup(active);

    const state = reducer(
      {
        ...initialState,
        filters: {
          ...initialState.filters,
          group: "daily"
        }
      },
      action
    );

    expect(state).toEqual({
      ...initialState,
      filters: {
        ...initialState.filters,
        group: "month"
      }
    });
  });

This test works however I want to know why I have to spread the initialstate once, and then again within the filters object I have to spread the initialState.filters again just to manipulate the month.

In my head this should work fine :

const state = reducer(
      {
        ...initialState,
        filters: {
          group: "daily"
        }
      },
      action
    );

My question is why do I need to put the line initialState.filters to make this test viable. Please can someone help me understand? I have done research but can't find anything.

Angela Inniss
  • 359
  • 1
  • 2
  • 18

2 Answers2

1

JavaScript's spread operator is fairly dumb; it doesn't attempt to do any kind of smart merging like some libraries offer. When you add the filters prop JavaScript simply overwrites initialState's spread filters definition.

You can think of your code sample as being roughly equivalent to

Object.assign(
  {},
  initialState,
  {
    filters: { ... }
  })
Chris Camaratta
  • 2,729
  • 22
  • 35
1

When you are doing ...initialState.filters you are spreading the filters object on initialState. You have to do this as you have just defined a new filters object in your reducer and so have to place in it the values you want to keep from the old version.

If this all seems a bit to verbose, then check out Immer, which allows you to have immutable objects with a bit more a traditional API

https://immerjs.github.io/immer/docs/introduction

David Bradshaw
  • 11,859
  • 3
  • 41
  • 70
  • This helps me understand. So just to clarify, when I spread the `initialState` object the first time I am making a new copy of the object as a whole. Then I have to say something like from this new `initialState` I want to keep the filters object (`...initialState.filters`) and the `group` value within it. – Angela Inniss Dec 08 '19 at 18:42
  • 1
    Almost, you have do the second one as you redefined `filters` when you add or change a value to it. – David Bradshaw Dec 08 '19 at 18:57