Replace array inside object - Reducer - javascript

I have an array of elements which was updated inside an action, now I want to update it in the store. Currently I have something like:
navigation
|_navigationItems:[{1:"foo"}, {2:"bar"}, {3:"foobar"}]
The thing is I was doing the following:
case types.UPDATE_NAVIGATION:
return Object.assign({}, state, {
navigationItems: action.payload.items,
});
where items is: [{1:"zoo"}, {2:"foobar"}]
but store was not updated succesfully.
Did I miss something?

React docs suggest on using the spread operator syntax over Object.assign
Use:
case types.UPDATE_NAVIGATION:
return {
...state, navigationItems: action.payload.items
}

Related

State Mutation detected when updating Redux Values

So I'm fairly new to React-Redux and I'm facing this problem where if i update this array with a new object in redux, i'm getting the following error
Uncaught Invariant Violation: A state mutation was detected between dispatches, in the path `settingsObject.arrayForSettings.0.updatedObject`. This may cause incorrect behavior.
The Problem only arises when i update the state in redux with new values, On previous values there is no error. The piece of code that is giving me the error is as follows.
let tempArrayForSettings = [...props.arrayForSettings];
tempArrayForSettings.forEach((element:any) => {
if(element.settingsType === "DesiredSettingType")
{
//modify elements of a JSON object and add it to the element
element.updatedObject = JSONUpdatedObject;
}
//call the action method to update the redux state
updateAction(tempArrayForSettings);
});
The error is pointing me to the following link : Redux Error
I know I'm not updating the object I'm getting from props instead I'm making a copy using the spread operator so I really don't know why the error is coming whenever I'm firing the updateAction function.
Well, your line element.updatedObject = JSONUpdatedObject; is modifying the object in the Redux store. element is a direct reference to your store object. You would need to do an immutable copy here - your spread above only does a shallow copy, but the items here are still the same.
Generally, you should do logic like this not in your component, but within your Reducer. (see the Style Guide on this topic) That also gives you the benefit that you don't need to care about immutability, as within createSlice reducers you can simply modify data.
You're updating the object inside the array, so the spread operator that you've created above only clones the array itself, not the objects inside the array. In Javascript, objects are passed by reference (read more here)
To fix this warning, you'd need to copy the object itself, and to do that, you'd need to find the specific object in the array that you'd like to change.
Try this:
const { arrayForSettings } = props;
const modifiedSettings = arrayForSettings.map((element:any) => {
if(element.settingsType === "DesiredSettingType")
{
//modify elements of a JSON object and add it to the element
return {
...element,
updatedObject: JSONUpdatedObject,
}
}
return element;
}
updateAction(modifiedSettings);
});
Also, it's recommended that this logic lives on the reducer side, not in your component.

Redux data not rerendering when setting value within object (Immutable.js)

I have an immutable.js Map stored in Redux that is structured like:
reduxObject: {
details: {}
...
objectToChange: {
myPosts: [{
name: 'someName',
link: 'someLink',
}],
drafts: []
}
}
I am trying to append the array objectToChange.myPosts in a reducer function using
let temp = state.getIn([objectToChange, myPosts])
temp.push(action.payloadData)
return state.setIn([objectToChange, myPosts], temp)
The redux data is getting updated, however the displayed redux data isn't getting rerendered. I was expecting the state.setIn to create a new immutable object causing react native to trigger a rerender. Any suggestions or help would be greatly appreciated. Thanks ahead of time
So I found a workaround to using immutable that i'm not sure is acceptable but it works. I am now using lodash's cloneDeep function.
let temp = _.cloneDeep(state)
temp[action.payloadType][action.payloadLoc].push(action.payloadData)
return (temp)
Ok so cloneDeep was a bad workaround but i found the proper solution to the problem. I needed to return the statement like this:
case 'postNewItemFbData':
let tempUpdate = state[action.payloadType][action.payloadLoc]
tempUpdate.push(action.payloadData)
return {
...state,
[`${action.payloadType}.${action.payloadLoc}`]: tempUpdate
}

Angular ngrx: TypeError: Cannot freeze array buffer views with elements

Im encountering an issue with ngrx. I have an array in my state to which i want to append objects. This actually works fine if i console.log them i can see that values in my store. But the redux dev tools and the console throw the error "TypeError: Cannot freeze array buffer views with elements".
I have a state that looks like this:
const state: State = {
array: []
};
The Object i pass to my actions looks similar to this:
const obj = {attr: number, data: ImageData};
Where ImageData comes from a Canvas and is extracted with canvas.getContext("2d").getImageData(...);. It should be noted that this Object is huge with over 69000 keys with values from 0 to 255(RGBA).
And i append the new Object to the state/store like this:
createReducer(
initialState,
on(action, (state: State, action)=> {
return {
...state,
array: [...state.array, action.payload]
}
})
);
Furthermore i read that i should deepCopy Objects before passing them to the action so i did that with lodashs copyDeep(), but i got the same results.
Any help is appreciated.
You need state.array in your reducer.
return {
...state,
array: [...state.array, action.payload]
}
I had the same problem, I solved it by cloning the data that I pass to the dispatch method
ex:
const dataCloned = CloneDataInDeep.clone(dataToAdd);
this.store$.dispatch(actionToDispatch({ dataCloned}));
Another option that I have tried is to change this attribute value "strictActionImmutability" in the ngrx configuration from true to false
runtimeChecks: {
strictActionImmutability: false,
....
}
I use Angular and came across that error.
For me, deep copying the array before the dispatch worked
const copyArray = JSON.parse(JSON.stringify(array));
this.store.dispatch(actionDispatch.requestSth({ myArray: copyArray }));
Not the most beautiful solution, I know.
Edit:
Unfortunately the solution was not helpful because the objects inside the array lose the functions when strigified... So back to square one
You can use this code. This will copy the entire object so the dependency will be removed.
const newlyCreatedArray = Object.assign({}, array);
For more details: Object.assign()

Prepend to nameless array in Redux reducer

I get an array of objects from an API and store it in an articleData prop via Redux. The prop is stored like this:
In my reducer I want to prepend to this array with another articleData object. The object I want to prepend is stored in action.articleData but I can't find a way to prepend this array without naming it. Here is my reducer code:
export function articleData(state = {}, action) {
switch (action.type) {
case 'ARTICLE_FETCH_DATA_SUCCESS':
return action.articleData;
//HERE IS THE PROBLEM!
case 'ARTICLE_POST_NEW_ARTICLE_SUCCESS':
return {arr:[action.articleData, ...state]}
default:
return state;
}
}
It prepends the new object successfully. Problem is: When I execute this code it changes the state from an array of objects to an array named "arr". See picture:
I can't figure out how to just add to the array of objects without naming it something. If I remove the arr: from the reducer code it results in a syntax error.
Thanks in advance to anyone willing to help me out! :)
The state of the articleData reducer is an array. In the ARTICLE_POST_NEW_ARTICLE_SUCCESS action handler, you create a new state, which is an object, with an arr property - {arr:[action.articleData, ...state]}.
Instead return a new array with the new articleData, and spread the state into it:
case 'ARTICLE_POST_NEW_ARTICLE_SUCCESS':
return [action.articleData, ...state]
In addition since the state of the reducer is an array, change the initial state to an empty array:
export function articleData(state = [], action) {

Extra fields are added to redux store because of immutable js, how to stop this?

Background
I have a react redux application making use of immutable js.
Problem
For the most part this is working perfectly but some of the reducers in the application are adding several extra fields to my redux store.
Example
The fields that I can see are as follows
_root
__altered
size
This only happens some of the time. When I use a reducer that also merges the current state.
case ActionType.SUCCESS_GET_DATA : {
let newState = { ...state, [action.meta]: action.payload };
return state.merge(newState);
}
where: action.meta is the unique key/name of the data and action.payload is the data that is successfully retrieved.
Question
So I can see that creating a new state with the spread operator is causing these extra fields to be added to my state. So is there a way to use the spread operator without adding these extra fields?
Immutable maps will always add there own 'meta'
So I have come to the conclusion that Immutable maps will always add there own meta to the state. This is because its a map merged with an object.
To get around this use the method toJS()
case ActionType.SUCCESS_GET_DATA : {
let jsState = state.toJS();
let newState = { ...jsState, [action.meta]: action.payload };
return state.merge(newState);
}
Now you're merging a object with an object.

Categories

Resources