I have a problem updating the old value of the array in my redux react app. I have successfully updated the new selected object true. I want to other object to set to false since I have set the another object to true.
const initialState = {
annualPlans: [],
};
const planReducer = (state = initialState, action) => {
switch (action.type) {
case planConstants.UPGRADE_PLAN_SUCCESS:
return {
...state,
annualPlans: state.annualPlans.map((todo) =>
todo.value === action.data.plan_id
? // transform the one with a matching id
{ ...todo, current: true }
: // otherwise return original todo
todo
),
};
default:
return state;
}
};
It seems like you want to return current set to false for the others:
todo.value === action.data.plan_id
? // transform the one with a matching id
{ ...todo, current: true }
: // otherwise return with current false
{ ...todo, current: false }
Id first create the new todos by looping though a map and then assign to the state
case planConstants.UPGRADE_PLAN_SUCCESS: {
const newTodos = state.annualPlans.map((todo) => {
if (todo.value === action.data.plan_id) {
return { ...todo, current: true }; // if todo.id matched then set the current to true and return;
}
if (todo.current) {
return { ...todo, current: false }; // else if current is already true, then false it and return
}
return todo; // else return original todo
});
return {
...state,
annualPlans: newTodos
};
}
.....
This will optimize the rendering and prevent of looping multiple times.
No need to re create all todo items, if you use pure components then that will mess up this optimisation. You can just add a map to deal with resetting the current value:
const planReducer = (state = initialState, action) => {
switch (action.type) {
case planConstants.UPGRADE_PLAN_SUCCESS:
return {
...state,
annualPlans: state.annualPlans
.map(
(todo) =>
todo.current
? { ...todo, current: false } //reset current to false
: todo // no need to change this one
)
.map((todo) =>
todo.value === action.data.plan_id
? // transform the one with a matching id
{ ...todo, current: true }
: // otherwise return original todo
todo
),
};
default:
return state;
}
};
Related
export const basketReducer = (state = { total:0, items:[]}, action) => {
switch (action.type) {
case "ADD_ITEM":
const item = [...state.items, action.payload]
const updateBasket = state.items.map(el => {
if (el._id === action.payload._id) {
return {
...el,
quantity: el.quantity + action.payload.quantity
}
}
return item
})
It seems your code is close.
First check if state.items array already contains some element with a matching _id property.
If there is a match then shallow copy the state and shallow copy the items array and update the matching element.
If there is no match then shallow copy the state and append the new data to the items array.
Reducer case logic:
case "ADD_ITEM":
const hasItem = state.items.some(el => el._id === action.payload._id);
if (hasItem) {
// update item
return {
...state:
items: state.items.map(el => {
if (el._id === action.payload._id) {
return {
...el,
quantity: el.quantity + action.payload.quantity
}
}
return el; // <-- return current mapped element if no change
}),
};
} else {
// add item
return {
...state,
items: state.items.concat(action.payload),
};
}
I have an array of objects. I find the object I need to replace but can't replace it in state. The original object stays untouched and the object I want to replace it with is added to the end of the array. I'm use React hooks. Thanks
case types.ADD_USERNAME:
let task = action.payload
let assignedTask = state.tasks.find(el => el.id === task.id)
let index = state.tasks.indexOf(assignedTask)
return {
...state,
tasks: [
...state.tasks,
assignedTask = {
...assignedTask,
user: task.user.name
}
]
}
You can make use of map instead of going by index and find element to update the state which in my opinion is a cleaner approach
case types.ADD_USERNAME:
let task = action.payload;
return {
...state,
tasks: state.tasks.map(el => {
if(el.id == task.id) {
return { ...el, user: task.user.name}
}
return el;
});
}
However if you wish to go by the index and find element approach, you need to slice the task array and update the specific index element
case types.ADD_USERNAME:
let task = action.payload
let index= state.tasks.findIndex(el => el.id === task.id);
return {
...state,
tasks: [
...state.tasks.slice(0, index - 1),
{
...state.tasks[index],
user: task.user.name
}
...state.tasks.slice(index + 1)
]
}
You could achieve this by using map and matching your object by id or any other unique identifier:
case types.ADD_USERNAME:
let task = action.payload;
return {
...state,
tasks: state.tasks.map(t => {
if (t.id === task.id) {
return {
...t,
user: task.user.name
};
}
return t;
})
};
I have been building an application using React, Redux, Redux-thunk.
While calling action, the payload and type are properly received by the reducer but it is not modifying them.
My Reducer:
import EditorActionTypes from './editor.types';
const INITIAL_STATE = {
editorModified: false,
tabs: [
{
name: 'main.js',
active: true
},
{
name: 'setup.js',
active: false
}
]
};
const editorReducer = ( state = INITIAL_STATE, action ) => {
switch (action.payload) {
case EditorActionTypes.SELECT_TAB:
return {
...state,
tabs: [
...state.tabs.map(
(tab, index) => index === action.payload
? tab.active = true
: tab.active = false
)
]
}
default:
return state;
}
};
export default editorReducer;
Switch condition is wrong,
Reducer function should check for action.type
switch (action.type) {
case EditorActionTypes.SELECT_TAB:
return {
The problematic part, I think, is this:
...state.tabs.map(
(tab, index) => index === action.payload
? tab.active = true
: tab.active = false
)
You are not returning anything from the function inside map. You should return tab objects, instead.
...state.tabs.map(
(tab, index) => ({
...tab,
isActive: index === action.payload
}))
And as #Gangadhar Gandi pointed out, you should switch on the action.type, not the action.payload
I need to create a reducer that toggles the state of done using the id or index of the todo
state = {
todos: [
{
title: "eat rice",
done: false,
id: 1
},
{
title: "go fishing",
done: true,
id: 2
},
{
title: "drink coffee",
done: false,
id: 3
}
]
}
I tried this but it mutates the state, the payload being the index of the object in the array.
case "DONE":
const todos = [...state.todos];
todos[action.payload].done = !todos[action.payload].done;
return {
...state,
todos: todos
};
You could use a map function instead. The function will generate a new array which you can use to replaces todos with.
case "DONE":
const newTodos = state.todos.map((todo, index) => {
// Based on your code, I assume action.payload is the index of the todo in the array of todos
if (index === action.payload) {
const newTodo = {...todo};
todo.done = !todo.done;
return todo;
}
return todo;
});
return {
...state,
todos: newTodos,
};
If you don't want to iterate over every todo, you could do something else such as using slice to create a copy of the array and then change the one value:
case "DONE":
const newTodos = todos.slice();
const updatedTodo = {...newTodos[action.payload]};
updatedTodo.done = !updatedTodo.done;
newTodos[action.payload] = updatedTodo;
return {
...state,
todos: newTodos,
};
Found the answer. Thanks for the contributions.
case "DONE":
const newTodos = state.todos.map((todo, index) => {
if (index === action.payload) {
const newTodo = { ...todo };
newTodo.done = !newTodo.done;
return newTodo;
}
return todo;
});
return {
...state,
todos: newTodos
};
Using the spread operator or map will create a new array but will not automatically clone the contained objects, as JavaScript follows "pass by reference". You'd have to clone the object as well. So maybe something like
case "DONE":
const todos = state.todos.map((todo, index) => {
const newTodo = {...todo};
if (action.payload === index) {
newTodo.done = !todo.done;
}
return newTodo;
});
return {
...state,
todos,
};
Of course you could also use a clone utility or something like Immutable.js.
I'm learning redux and I was wondering how to delete one item from the state. I have this initial state:
export const getInitialState = () => {
let state = {
isLogged: false,
organizations: [],
userData: {},
activeIndex: -1,
currentRetrospective: {},
hasFetched: false
}
This is how the data lives inside organizations
case `${actions.ACTION_GET_USER_ORGS}_FULFILLED`: {
let activeIndex = 0
if (state.activeIndex !== -1) {
activeIndex = state.activeIndex
} else if (action.payload.data.length === 0) {
activeIndex = -1
}
return { ...state, activeIndex, organizations: action.payload.data, hasFetched: true }
}
Now, what I need to do is to delete one item from the retrospectives array in an organization. I tried this but it doesn't work. Is there a better way to do it?
export default (state = getInitialState(), action) => {
switch (action.type) {
case `${actions.ACTION_DELETE_RETROSPECTIVE}_FULFILLED`: {
const { organizations, activeIndex } = state
const newOrganizations = JSON.parse(JSON.stringify(organizations))
const activeOrganization = newOrganizations[activeIndex]
activeOrganization.retrospectives = activeOrganization.retrospectives
.filter((retro) => retro.id != action.retroId )
return { ...state, organizations: newOrganizations }
}
Thank you!
you can filter the organization array like this:
export default (state = getInitialState(), action) => {
switch (action.type) {
case `${actions.ACTION_DELETE_RETROSPECTIVE}_FULFILLED`: {
return {
...state,
organizations: state.organization.filter(retro =>
retro.id !== action.retroId }
}