Prepend to nameless array in Redux reducer - javascript

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

Related

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

Replace array inside object - Reducer

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
}

Redux mapStateToProps returns undefined

Redux is successfully storing and updating state. The reducers are seemingly working correctly. I'm able to use this.props.dispatch. However, when it actually comes to detailing that information (i.e. this.props.array I always seem to get undefined.
Reducer:
export default function array(state = {}, action) {
switch (action.type) {
case "UPDATE_ARRAY":
state.array = action.array
return state;
default:
return state;
}
}
State-aware component:
constructor(props) {
super(props);
this.state = {
array: array
}
}
--
self.props.dispatch({
type: 'UPDATE_ARRAY',
array: array
})
--
const mapStateToProps = (state) => {
return {
messages: state.messages,
array: state.array
};
};
export default connect(mapStateToProps)(Component);
This only seems to be able to save state btw when I define an empty array. This doesn't seem right, I thought the intention of Redux was a self-contained store? Updating a variable seems to defeat the purpose a bit.
Would appreciate any help.
export default function array(state = {}, action) {
switch (action.type) {
case "UPDATE_ARRAY":state={
...state,
array:action.array
}
return state;
default:
return state;
}
}
you should always update your state immutably,instead of mutating the current application state ,you should create another object and return that.State should be immutable ,only way to change the state is to create a new one.This helps to improve the performance of the application.
I am not sure if you application has more than one reducer or not, if it has, than you must be using combine reducer method .So to access state.array in mapsStateToProps is like this
const mapStateToProps = (state) => {
return {
messages: state.{reducer_name}.message,
array: state.{reducer_name}.array
};
};
in place of 'reducer_name' you have to specify the reducers_name which you have define in combine reducer
And last mapStateToProps return array ,in props not in component state.
which you can access in this way {this.props.array},you cant set component state in componentDidMount and in componentWillRecieveProps (in case of aysnc action).
Your component will receive array as a field in its props field. Your code assumes it's in the state field. So instead of:
this.state = {
array: array
}
you would just access this.props.array wherever in your code you need to use the array. You don't need to put it in the local state at all. Usually, you would use it in the render function, like in this example:
render()
{
return <div>The array contains {this.props.array.length} items.</div>
}
I wonder if you're confusing local state with the Redux store's state? Local state is what you get/set when you access this.state in your component code. Every component can have its own state object that it can read from and write to.
The Redux store's state is what's passed in to mapStateToProps. It's usually the entire state object of all the combined reducers in your top-level reducer (though if you only have one reducer function and are not using combineReducers, then the store state is identical to that single reducer's state).
I suggest choosing more descriptive variable names, so that your code will be more readable. It's hard to understand what your intentions are for your code with such generic names. For example, you could name your reducer something that indicates what it's for, like bookListReducer, and name the array you want to store and retrieve for what will go inside it, like books. Naming both your reducer and all your variables array makes it harder to read your code. This will help anyone who reads your code in the future - including, most importantly, you!, as well as future Stack Overflow readers of your future questions (and perhaps this one if you edit it).
I am not sure the following is your issues, but hope these will help:
export default function array(state = {}, {type, array}) {
switch (type) {
case "UPDATE_ARRAY":
return {...state, array};
default:
return state;
}
}
Your reducer should be pure, which you had is mutating the state.
constructor(props) {
super(props);
this.state = {
array: array // what is array?
}
}
Above constructor is not right. You should be able to access the array from this.props.array as your mapStateToProps
Do a console.log(this.props) in your render function or ComponentWillReceiveProps, see if you can something :)

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.

Redux: what is the correct way to filter a data array in reducer?

i want to filter an array on search SEARCH_TEXT is an on change action
what I'm confused with is how i return the state when the delete key is pressed and the text now becomes empty, i figure i could use initial state in the else statement but my inclination is this is wrong? when i return just state it has all ready been manipulated in the if statement.
simple example.
thanks in advance.
const initialState = ['hello', 'wahhh', 'yo'];
export default function searchSimple(state = initialState, action) {
switch (action.type) {
case SEARCH_TEXT:
if(action.text.length > 0){
return state.filter(item =>
item.startsWith(action.text)
)
}
else {
return state
}
Remember always that the state is your "source of truth". Be wary of eliminating state on the basis of a temporary filter. Once you do so those items are gone. (The only way to get them back is to reset your state to the initialState, which may not be ideal.)
A better approach is to keep your items list as is, and simply store the search text.
const initialState = {
searchText: '',
items: [ 'hello', 'wahhh', 'yo' ]
};
export default function searchSimple(state = initialState, action) {
switch (action.type) {
case SEARCH_TEXT:
return Object.assign({}, state, {
searchText: action.text
});
}
}
Whilst your state won't contain the filtered list, it tells you everything you need to know to construct the filtered list.
Assuming you're using React, your "smart component" can be setup with the following mapStateToProps() function:
function mapStateToProps(state) {
const { items, searchText } = state.searchSimple;
return {
filteredItems: items.filter((item) => item.startsWith(searchText))
};
}
Should you need this filtered list in more than one place, consider creating a "selector" function, as demonstrated in the Redux shopping cart example.
https://github.com/reactjs/redux/blob/master/examples/shopping-cart/src/reducers/cart.js
It would look something like this:
export function filteredItems(state) {
const { items, searchText } = state.searchSimple;
return items.filter((item) => item.startsWith(searchText));
}
For a more advanced approach to selectors, check out the reselect library.
https://github.com/rackt/reselect
IMO, the right place to filter data is not directly in the reducers but in the selectors.
From redux docs:
Computing Derived Data
Reselect is a simple library for creating memoized, composable selector functions. Reselect selectors can be used to efficiently compute derived data from the Redux store.
I'm currently using selectors to filter and sort data.
No data repetition in the state. You don't have to store a copy of the filtered items.
The same data can be used in different components, each one using a different filter for example.
You can combine selector applying many data computations using selector that you already have in the application.
If you do right, your selectors will be pure functions, then you can easily test them.
Use the same selector in many components.

Categories

Resources