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

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()

Related

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
}

How to clear array which is inside nested state?

I have crated state like below and at some point I want to clear this array and update with new one. I used below code for clearing, but when I check state through react-developer tool, array is still there nothing is happening. Please help me as my newbie to ReactJS.
state = {
formData:{
level:{
element:'select',
value:'Bachelors',
label:true,
labelText:'Level',
config: {
name:'department',
options: [
{val :"Bachelors", text:'Bachelors'},
{val:"Masters", text:'Masters'}
]
},
validation: {
required:false,
}
,
valid:true,
touched:false,
validationText:'',
},
Now I want to clear options array by:
let {options} = this.state.formData.level.config
options = []
this.setState({
options:options
})
But my array is not clearing. How can I achive that?
Two things that you should know.
1) When you do this.setState({ options: options }), you are not updating the options array belonging to the formData object. You are creating a brand new key-value pair in your state called options. If you try to print your updated state, you will find that your state now looks like:
state = { formData: {...}, options: [] }
2) You are trying to directly mutate state, (changing a value belonging to the existing object). This is against React principles, because in order for the component to re-render correctly without being prone to side-effects, you need to provide a brand new state object.
Since its so deeply nested, the likely best option is to deep clone your state-object like this:
const newFormData = JSON.parse(JSON.stringify(this.state.formData))
newFormData.level.config.options = []
this.setState({
formData: newFormData
})
This will probably be the safest way to clear your form data object. Here's a working sandbox with this feature as well. Please use it for reference:
https://codesandbox.io/s/naughty-cache-0ncks
create newstate and change properties then apply the new state to setState() function like this:
changestate = ()=>{
var newstate = this.state;
newstate.formData.level.config.options=["hello","wow"]
this.setState(newstate)
}
You are not clearing the options state but creating a new state with an empty array.
You can either clone the whole object using JSON stringify and JSON parse too . Other way to update nested states without mutation is by using spread operators. The syntax looks a bit complex though as spread nested objects while preserving everything is tricky.
const newFormData = {
...this.state.formData,
level: {
...this.state.formData.level,
config: {
...this.state.formData.level.config,
options: []
}
}
};
this.setState({
formData: newFormData
})

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) {

Updating Object Property in Reducer without Mutation

I feel like my reducer should be working, but it keeps insisting that I'm mutating the state.
Uncaught Error: A state mutation was detected inside a dispatch, in the path: output.outputList.0.composition. Take a look at the reducer(s) handling the action {"type":"SET_OUTPUT_COMPOSITION",
I posted something similar a couple hours ago with no answers, but I figured my redux state was too complicated. This is my simplified version and I'm still getting mutate errors.. what am I doing wrong? should I not be using a class in my redux state? should i be using some sort of immutable library? please help me.
My Initial Redux State
output: {
outputList: [], //composed of Output class objects
position: 0
}
Output Class
class Output {
constructor(output) {
this.id = output.id;
this.composition = output.getComposition();
this.outputObj = output;
this.name = output.name;
this.url = output.getUrl();
}
}
export default Output;
Reducer for updating property
case types.SET_OUTPUT_COMPOSITION: {
let outputListCopy = Object.assign([], [...state.outputList]);
outputListCopy[state.position].composition = action.composition;
return Object.assign({}, state, {outputList: outputListCopy});
Action
export function setOutputComposition(comp) {
return { type: types.SET_OUTPUT_COMPOSITION, composition: comp}
}
The spread operator does not deep copy the objects in your original list:
let outputListCopy = Object.assign([], [...state.outputList]);
It is a shallow copy, therefore
outputListCopy[state.position].composition = action.composition;
You are actually mutating previous state objects, as you said in your comment there are several ways to work around this, using slice/splice to create new instance of the array, etc.
You can also take a look at using ImmutableJS, in general I would say storing classes in the redux store makes the thing a bit hard to understand, I tend to favor simple structures that can be easily inspected with redux-tools.
The error is coming from dispatch. So it not even getting as far as the reducer. I expect it does not like you using class to define output. Instead just do const output ={ ... }.

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