The initial state looks like this:
const INITIAL_STATE = {
myArray: []
};
Now in my reducer, I want to append a new object to the existing array.
I came up with something like this but it doesn't work as expected.
case ADD_TO_ARRAY:
return {
...state,
myArray: [...state[ { action.payload.key: action.payload.value} ]]
};
Note: I want to create a new object, in line, using the key and value passed in the action payload.
with ES6 you can have dynamically calculated object keys
just add a variable to be evaluated in square brackets []
case ADD_TO_ARRAY:
return {
...state,
myArray: [...state.myArray, [ { [action.payload.key]: action.payload.value} ]]
};
Related
For reference this is the initial state:
const initialState = {
dogs: [],
cats: [],
petToPreview: {},
petToAdopt: {},
};
I have the following case that I am trying to solve
case 'ADD_NEW_DOG':
let pleaseWork = {
...state,
dogs: [action.dog],
};
console.log(pleaseWork);
The action creator I am using is this:
export const addNewDog = (pet) => {
return {
type: 'ADD_NEW_DOG',
dog: pet,
};
};
The question I am trying to solve is that this case adds the new dog to the end of the dogs array (without mutating the previous state). The way that I have it set up right now, is that it adds the action correctly, However, every time a new 'dog' tries to get added, it just overwrites the previous one.
When I log out my case I get this:
{
dogs: [ { id: 1, name: 'Taylor' } ],
cats: [],
petToPreview: {},
petToAdopt: {}
}
However, Like I mentioned any new action that gets added, overwrites this. I have tried pushing into the 'clone' however this I know mutates the array, so I do not know where to go from here.
You need to clone dogs array and add new entry at the end.
case 'ADD_NEW_DOG':
let pleaseWork = {
...state,
dogs: [...state.dogs, action.dog],
};
console.log(pleaseWork);
You replace the new array with the older one,
case 'ADD_NEW_DOG':
let pleaseWork = {
...state,
dogs: [...state.dogs,action.dog],
};
I cannot seem to find an answer on here that is relevant to this scenario.
I have my state in my React component:
this.state = {
clubs: [
{
teamId: null,
teamName: null,
teamCrest: null,
gamesPlayed: []
}
]
}
I receive some data through API request and I update only some of the state, like this:
this.setState((currentState) => {
return {
clubs: currentState.clubs.concat([{
teamId: team.id,
teamName: team.shortName,
teamCrest: team.crestUrl
}]),
}
});
Later on I want to modify the state value of one of the properties values - the gamesPlayed value.
How do I go about doing this?
If I apply the same method as above it just adds extra objects in to the array, I can't seem to target that specific objects property.
I am aiming to maintain the objects in the clubs array, but modify the gamesPlayed property.
Essentially I want to do something like:
clubs: currentState.clubs[ index ].gamesPlayed = 'something';
But this doesn't work and I am not sure why.
Cus you are using concat() function which add new item in array.
You can use findIndex to find the index in the array of the objects and replace it as required:
Solution:
this.setState((currentState) => {
var foundIndex = currentState.clubs.findIndex(x => x.id == team.id);
currentState.clubs[foundIndex] = team;
return clubs: currentState.clubs
});
I would change how your state is structured. As teamId is unique in the array, I would change it to an object.
clubs = {
teamId: {
teamName,
teamCrest,
gamesPlayed
}
}
You can then update your state like this:
addClub(team) {
this.setState(prevState => ({
clubs: {
[team.id]: {
teamName: team.shortName,
teamCrest: teamCrestUrl
},
...prevState.clubs
}
}));
}
updateClub(teamId, gamesPlayed) {
this.setState(prevState => ({
clubs: {
[teamId]: {
...prevState.clubs[teamId],
gamesPlayed: gamesPlayed
},
...prevState.clubs
}
}));
}
This avoids having to find through the array for the team. You can just select it from the object.
You can convert it back into an array as needed, like this:
Object.keys(clubs).map(key => ({
teamId: key,
...teams[key]
}))
The way I approach this is JSON.parse && JSON.stringify to make a deep copy of the part of state I want to change, make the changes with that copy and update the state from there.
The only drawback from using JSON is that you do not copy functions and references, keep that in mind.
For your example, to modify the gamesPlayed property:
let newClubs = JSON.parse(JSON.stringify(this.state.clubs))
newClubs.find(x => x.id === team.id).gamesPlayed.concat([gamesPlayedData])
this.setState({clubs: newClubs})
I am assuming you want to append new gamesPlayedData each time from your API where you are given a team.id along with that data.
I'm attempting to get my redux reducer to perform something like this:
.
however, I am outputting the following:
The following is the code I am using to attempt this. I've attempted to include action.userHoldings in the coinData array, however, that also ends up on a different line instead of within the same object. My objective is to get userHoldings within the 0th element of coinData similar to how userHoldings is in the portfolio array in the first image.
import * as actions from '../actions/fetch-portfolio';
const initialState = {
coinData: [],
userHoldings: ''
};
export default function portfolioReducer(state = initialState, action) {
if (action.type === actions.ADD_COIN_TO_PORTFOLIO) {
return Object.assign({}, state, {
coinData: [...state.coinData, action.cryptoData],
userHoldings: action.userHoldings
});
}
return state;
}
I guess you want to do something like this.
return Object.assign({}, state, {
coinData: [ {...state.coinData[0],cryptoData: action.cryptoDatauserHoldings: action.userHoldings}, ...state.coinData.slice(1) ],
});
}
slice(1) is to get all elements except 0th. For the first element, you can construct object the way you like. This should do the trick. Slice returns a new array unlike splice/push or others so safe to use in reducers. :)
I have a project, where I use react-redux, and I have a reducer, which by idea should add one element in array and return new array. How I can do this?
/*---- Reducer ----*/
case CHANGE_EVENT_USERS:
return { ...state, users: payload };
/*---- Here's my hopeless tryings ----*/
userClickHandler() {
const { id, homeFloor, avatarUrl, login } = this.props;
const user = { id, homeFloor, avatarUrl, login };
this.props.changeEventUsers([...user]); // []
this.props.changeEventUsers(this.props.event.users.push()); // number
}
I'm not sure if I understand you correctly but from my understanding the solution to your problem would look something like this:
case CHANGE_EVENT_USERS:
return { ...state, users: [ ...state.users, action.payload ] };
I like better the syntax of concat.
In your reducer do:
case CHANGE_EVENT_USERS:
return users.concat(action.payload);
Do the add directly in the reducer.
From your component
this.props.changeEventUsers(newUser); // add the new user
In the reducer
return { ...state, users: [...state.users, payload] };
I made the assumption that "payload" contains the info coming from the action and the users array is by default initialised with an empty array value []
Use concat()
const initialArray = [1, 2, 3];
const elemenToAdd = 4;
const newArray= initialArray.concat([elementToAdd]);
[1, 2, 3, 4]
Notice I'm using const here to emphasize that the initial array was not mutated.
The great thing about the method above, it's that it can be used to chain operations together.
result = initialArray.concat(..).filter(..).concat(..);
(where .. represents skipped code details)
You can also use concat by passing in arrays as parameters:
newArray = concat(initialArray, [elementToadd])
Or use es7 spread operator syntax for array concatenating:
newArray = [...initialArray, elementToAdd, ...[5], [6] ];
[1, 2, 3, 4, 5, [6] ]
Use ... to send individual elements of the supplied array to be elements in the new array; without the dots, the supplied array is itself the element.
So in the your case, the line in question could be written as:
I found my solution:
this.props.changeEventUsers([...this.props.event.users, user]);
I have associative array.
It's a key(number) and value(object).
I need to keep state of this array same as it is I just need to update one object property.
Example of array:
5678: {OrderId: 1, Title: "Example 1", Users: [{UserId: 1}, {UserId: 2}, {UserId: 3}]}
5679: {OrderId: 2, Title: "Example 2", Users: [{UserId: 1}, {UserId: 2}, {UserId: 3}]}
I need to update Users array property.
I tried this but it doesn't work:
ordersAssociativeArray: {
...state.ordersAssociativeArray,
[action.key]: {
...state.ordersAssociativeArray[action.key],
Users: action.updatedUsers
}
}
This is data inside reducer.
What I did wrong how to fix this?
Something that might help.
When I inspect values in chrome I check previous value and value after execution of my code above:
Before:
ordersAssociativeArray:Array(22) > 5678: Order {OrderId: ...}
After:
ordersAssociativeArray: > 5678: {OrderId: ...}
Solution (code in my reducer)
let temp = Object.assign([], state.ordersAssociativeArray);
temp[action.key].Users = action.updatedUsers;
return {
...state,
ordersAssociativeArray: temp
}
So this code is working fine.
But I still don't understand why? So I have solution but would like if someone can explain me why this way is working and first not?
If it could help here how I put objects in this associative array initialy:
ordersAssociativeArray[someID] = someObject // this object is created by 'new Order(par1, par2 etc.)'
What you are doing is correct, as demonstrated by this fiddle. There may be problem somewhere else in your code.
Something that I would recommend for you is to separate your reducer into two functions, ordersReducer and orderReducer. This way you will avoid the excessive use of dots, which may be what caused you to doubt the correctness of your code.
For example, something like:
const ordersReducer = (state, action) => {
const order = state[action.key]
return {
...state,
[action.key]: orderReducer(order, action)
}
}
const orderReducer = (state, action) => {
return {
...state,
Users: action.updatedUsers
}
}
I hope you find your bug!
Update
In your solution you use let temp = Object.assign([], state.ordersAssociativeArray);. This is fine, but I thought you should know that it is sometimes preferable to use a {} even when you are indexing by numbers.
Arrays in javascript aren't great for representing normalized data, because if an id is missing the js array will still have an undefined entry at that index. For example,
const orders = []
array[5000] = 1 // now my array has 4999 undefined entries
If you use an object with integer keys, on the other hand, you get nice tightly packed entries.
const orders = {}
orders[5000] = 1 // { 5000: 1 } no undefined entries
Here is an article about normalizing state shape in redux. Notice how they migrate from using an array in the original example, to an object with keys like users1.
The problem can be that you're using array in the state but in the reducer you're putting as object. Try doing:
ordersAssociativeArray: [ //an array and not an object
...state.ordersAssociativeArray,
[action.key]: {
...state.ordersAssociativeArray[action.key],
Users: action.updatedUsers
}
]
It will put ordersAssociative array in your state and not an object.