Reputation: 6481
I have a react-redux
application, and I have a reducer named dataReducer
that has a default state like this:
const defaultState = {
isLoading: false,
data: [{
id: 1,
label: 'abc',
elements: [{ color: 'red', id: 1}],
}],
};
One of the reducers adds elements to data
in the defaultState
. I need to test this reducer by passing the payload and then validating the new state. I want to use the spread operator to build the new state from the old defaultState
, but I am having some trouble achieving it. I tried the following, but it's not working:
const newElement = {
colour: 'blue',
id: 1
};
const newState = [
{
...defaultState.data[0],
elements: [{
...defaultState.data[0].elements,
...newElement,
}]
}
];
expect(dataReducer(defaultState, action)).toEqual(newState); // returns false
It would be great if I could somehow avoid using array index (defaultState.data[0]
) as there might be multiple objects in the defaultState
array in the real application, though for the purpose of testing, I am keeping just one object to keep things simple.
Upvotes: 2
Views: 162
Reputation: 1074475
If you're adding to the end, you spread out the other aspects of state in the new state object, then override data
with the current contents of it followed by the new entry:
const newState = { // New state is an object, not an aray
...defaultState, // Get everything from defaultState
data: [ // Replace `data` array with a new array
{
...defaultState.data[0], // with the first item's contents
elements: [ // updating its `elements` array
...defaultState.data[0].elements,
newElement
]
},
...defaultState.data.slice(1) // include any after the first (none in your example)
]
};
Live Example:
const defaultState = {
isLoading: false,
data: [{
id: 1,
label: 'abc',
elements: [{ color: 'red', id: 1}],
}],
};
const newElement = {
colour: 'blue',
id: 1
};
const newState = { // New state is an object, not an aray
...defaultState, // Get everything from defaultState
data: [ // Replace `data` array with a new array
{
...defaultState.data[0], // with the first item's contents
elements: [ // updating its `elements` array
...defaultState.data[0].elements,
newElement
]
},
...defaultState.data.slice(1) // include any after the first (none in your example)
]
};
console.log(newState);
.as-console-wrapper {
max-height: 100% !important;
}
There's no getting around specifying the entry in data
that you want (e.g., data[0]
).
In a comment you've asked how to handle this:
Let's say
data
(present insidedefaultState
) has multiple objects entries in it. First object hasid
of 1, second one hasid
of 2. Now thenewElement
to be added has anid
of 2. So thenewElement
should get added to the second object. Where in second object? Inside the elements property of the second object. The addition should not over-write existing entries in the elements array.
You'll need to find the index of the entry in data
:
const index = defaultState.data.findIndex(({id}) => id === newElement.id);
I'm going to assume you know that will always find something (so it won't return -1
). To then apply that index
to the code above, you'd do this:
const newState = { // New state is an object, not an aray
...defaultState, // Get everything from defaultState
data: [ // Replace `data` array with a new array
...defaultState.data.slice(0, index), // Include all entries prior to the one we're modifying
{
...defaultState.data[index], // Include the entry we're modifying...
elements: [ // ...updating its `elements` array
...defaultState.data[index].elements,
newElement
]
},
...defaultState.data.slice(index + 1) // include any after the one we're updating
]
};
The only real change there is adding the ...defaultState.data.slice(0, index)
at the beginning of the new data
, and using index
instead of 0
.
Live Example:
const defaultState = {
isLoading: false,
data: [
{
id: 1,
label: 'abc',
elements: [{ color: 'red', id: 1}],
},
{
id: 2,
label: 'def',
elements: [{ color: 'green', id: 2}],
},
{
id: 3,
label: 'ghi',
elements: [{ color: 'yellow', id: 3}],
}
],
};
const newElement = {
colour: 'blue',
id: 2
};
const index = defaultState.data.findIndex(({id}) => id === newElement.id);
const newState = { // New state is an object, not an aray
...defaultState, // Get everything from defaultState
data: [ // Replace `data` array with a new array
...defaultState.data.slice(0, index), // Include all entries prior to the one we're modifying
{
...defaultState.data[index], // Include the entry we're modifying...
elements: [ // ...updating its `elements` array
...defaultState.data[index].elements,
newElement
]
},
...defaultState.data.slice(index + 1) // include any after the one we're updating
]
};
console.log(newState);
.as-console-wrapper {
max-height: 100% !important;
}
Upvotes: 1