I have a reducer and trying to update 1 item in the statearray. It looks like this:
const players = (state = [{name:'John',nrGames:0,nrWins:0},{name:'Ed',nrGames:0,nrWins:0},{name:'Mark',nrGames:0,nrWins:0}], action) => {
switch (action.type) {
case 'ADD_PLAYER':
return [...state,{name:action.name}]
case 'ADD_WIN':
return [...state, action.name == 'bla' ? {nrWins:10} : {} ]
default:
return state;
}
};
export default players;
I am trying to figure out how to change the nrWins property for a certain name. So when ADD_WIN is dispatched with name='John' how to just update the John object and up the nrWins property with 1 and not the other objects in the state?
You need to .map over the players and when you find the one the action is describing create a new player with the updated number of wins. For readability I created a function called incWinForPlayer.
const incWinForPlayer = (name) => (player) => {
return player.name === name
? {...player, nrWins: player.nrWins + 1}
: player
};
const players = (state, action) => {
switch (action.type) {
case 'ADD_PLAYER':
return [...state, {name: action.name}]
case 'ADD_WIN':
return state.map(incWinForPlayer(action.name));
default:
return state;
}
};
export default players;
The "recommended" approach is to slice the old array up to the new item, concat it with the modified one, and concat it with the rest of the array.
Make sure that your action returns the whole new item, and the index you want to modify.
Something like:
case 'ADD_WIN':
return [
...array.slice(0, action.index),
action.item,
...array.slice(action.index)
];
Edit 1: source https://redux.js.org/docs/recipes/reducers/ImmutableUpdatePatterns.html#inserting-and-removing-items-in-arrays
Related
I am working on my reducer and I am trying to get the case for UPDATE working.
I am just trying to update the list on my react frontend when a certain object ID changes in the state. How can I make it so the item with a certain ID changes on the frontend in this reducer.
I tried this in UPDATE_ANIMALS, but no luck
case 'UPDATE_ANIMALS':
return [
...state, item => item.id === action.payload.id ? action.payload : item
];
Below is my entire reducer.
export const ANIMALSReducer = (state = [], action) => {
switch (action.type) {
case 'FETCH_ANIMALS':
return action.payload;
case 'ADD_ANIMALS':
return [...state, action.payload];
case 'DELETE_ANIMALS':
return [
...state.filter(item => item.id !== action.payload)
];
case 'UPDATE_ANIMALS':
return [
NOT SURE WHAT TO PUT HERE
];
default:
return state;
}
};
You can achieve that very easily using the map function, basically you're generating a new array and the matching item given your payload will be updated with the payload instead the original:
case 'UPDATE_ANIMALS':
return state.map(item => item.id === action.payload.id ? action.payload : item);
Hey everyone probably a simple question, basically I have a button when i click it fires an action and passes down the whole object that I concat to array if its not duplicate but strangely what happens because I save data to local-storage and after I load it from there it does not check for duplicate and duplicates the array item. My reducer code below maybe the error is there?
Searched as much as possible.
const initialState = {
favourites: []
};
const favourites = (state = initialState, action) => {
const { payload } = action;
switch (action.type) {
case actionTypes.ADD_FAVOURITES:
return {
...state,
favourites:
state.favourites.indexOf(payload) === -1
? state.favourites.concat(payload)
: state.favourites
};
default:
return state;
}
};
The issue here seems to be that state.favourites.indexOf(payload) === -1 is always true. This is because the function Array.prototype.findIndex() does not find identical objects.
You should use an alternate method of checking to see if the payload object is already in the favourites array. For example, you could try something like this:
const favourites = (state = initialState, action) => {
const { payload } = action;
switch (action.type) {
case actionTypes.ADD_FAVOURITES:
return {
...state,
favourites:
JSON.stringify(state.favourites).indexOf(JSON.stringify(payload)) === -1
? state.favourites.concat(payload)
: state.favourites
};
default:
return state;
}
};
I am trying to make a todo app using redux and I'm stack on how to delete a todo from the array.
reducer.js
export default function todo(state, action) {
switch (action.type) {
case 'ADD_TODO':
return [
...state,
{
id: action.id,
text: action.text,
completed: false
}
case 'REMOVE_TODO':
return {
id: action.id,
...state.slice(id, 1)
}
default:
return state;
}
}
action.js
let nextTodoId = 0
export const addTodo = text => ({
type: 'ADD_TODO',
id: nextTodoId++,
text
})
export const removeTodo = id => {
type: 'REMOVE_TODO',
id
}
So far i can add and toggle a todo as completed or not. Thanks
Using redux you need to return all array elements except the removed one from reducer.
Personally, I prefer using the filter method of the Array. It'll return you a shallow copy of state array that matches particular condition.
case 'REMOVE_TODO':
return state.filter(({id}) => id !== action.id);
In react redux application, you should know, you always have to create a new object,
to deleting an item please use spread operator like this :
return [...state.filter(a=>a.id !== id)]
I am looking at redux and adding names to an array. The code below works (kind of!).
I have a few issues.
I know that it is advised to create a new state tree object each time the state is passed through the reducer, however I thought it should still work even if I change the state object passed in.
In my code below the console.log(store.getState()); works if I use var newArr = state.names.concat(action.name); but not if I use state.names.push(action.name);
If I add another store.dispatch(action) the code doesn't work.
store.dispatch({type: 'ADD_NAME',name: 'PhantomTwo'});
Can anyone explain why this is so?
Finally, do I need to return state again outside the switch statement?
Here is the code I currently have below.
const initialState = {
names: []
}
function namesApp(state = initialState, action) {
switch(action.type) {
case 'ADD_NAME':
var newArr = state.names.concat(action.name);
return newArr;
default:
return state;
}
}
let store = createStore(namesApp);
store.dispatch({
type: 'ADD_NAME',
name: 'Phantom'
});
console.log(store.getState()); //returns `["Phantom"]`
This is the behavior of array object mutability
Since React highly cares about state change for re-rendering, so we need to take care of mutability.
The below snippet explains the array mutability.
let x = [];
let y = x;
console.log(x);
console.log(y);
y.push("First");
console.log(x);
console.log(y);
let z = [...x]; //creating new reference
console.log(z);
x.push("Second");
console.log(x); //updated
console.log(y); //updated
console.log(z); //not updated
So for better functionality your reducer will be like
function namesApp(state = initialState, action) {
switch(action.type) {
case 'ADD_NAME':
return {
...state, //optional, necessary if state have other data than names
...{
names: [...state.names, action.name]
}
};
default:
return state;
}
}
[].concat returns a new array. But your state was { name: [] }. Inspite of returning newly build object with new names, the code above returned the new names array.
Vanilla solution
const initialState = { names: [] };
function namesApp(state = initialState, action) {
switch(action.type) {
case 'ADD_NAME':
var newArr = state.names.concat(action.name);
return {
...state,
names: newArr
};
default:
return state;
}
}
immutability-helper
For this type of work I would use immutability-helper
import u from 'immutability-helper';
function namesApp(state = initialState, action) {
switch(action.type) {
case 'ADD_NAME':
return u(state, {
names: {
$push: action.name
}
});
default:
return state;
}
}
learn how to use immutability-helper https://facebook.github.io/react/docs/update.html
I'am trying to understand the example named "shopping cart" giving for redux :
https://github.com/reactjs/redux/tree/master/examples/shopping-cart
In this example you can add elements to your list of items, I tried to implement the function of remove a list of items :
But in the reducers folder there is an addedIds() function, I added a case to remove the element of the list but I don't know how to implement that, here is the function : the reste of my code is working fine I just don't know how to delete the product id from the array of addedIds.
const initialState = {
addedIds: [],
quantityById: {}
};
function addedIds(state = initialState.addedIds, action) {
switch (action.type) {
case ADD_TO_CART:
console.log("added ADD");
if (state.indexOf(action.productId) !== -1) {
return state
}
return [ ...state, action.productId ];
case REMOVE_TO_CART:
console.log("removed ADD");
// here is my problem
default:
return state
}
}
I assumed I need to do something like here :
Is this the correct way to delete an item using redux?
but I don't know how
Can you help me please ?
You can delete some element from array just filtering it out:
// ... skipped other cases from the switch
case REMOVE_TO_CART:
return state.filter(productId => action.productId !=== productId)
Approach with .filter() function looks very short and produces a new array instance as it required by redux.
For those who have a similar problem here is the solution :
const initialState = {
addedIds: [],
quantityById: {}
};
function addedIds(state = initialState.addedIds, action) {
switch (action.type) {
case ADD_TO_CART:
console.log("added ADD");
if (state.indexOf(action.productId) !== -1) {
return state
}
return [ ...state, action.productId ];
case REMOVE_TO_CART:
console.log("removed ADD");
return [ ...state.slice(0,state.indexOf(action.productId),
...state.slice(state.indexOf(action.productId)+1))
];
default:
return state
}
}
thanks to Josh Deeden who found this vidéo :
https://egghead.io/lessons/javascript-redux-avoiding-array-mutations-with-concat-slice-and-spread