redux array not updating correctly - javascript

In redux I have an array of selectedItems and changedItems
selectedItems: ["element 0","element 1","element 2"]
changedItems: ["element 6","element 7","element 8"]
<Button onClick={() => this.props.addItem(this.props.changedItems)}>save</Button>
I have a button that fires the addItem action but this is pushing 1 element into the selectedItems array from the changedItems array instead of all 3 elements and also I would like it to completely remove any existing elements in the selectedItems array when this is done
How can I push all elements from the changedItems array into the selectedItems array and overwrite the existing items in the selectedItems array?
actions.js
export const addItem = (item) => ({
type: ADD_ITEM,
item,
})
export const removeItem = (item) => ({
type: REMOVE_ITEM,
item,
})
export const clearItems = () => ({
type: CLEAR_ITEMS,
})
export const addChangedItem = (item) => ({
type: ADD_CHANGED_ITEM,
item,
})
export const removeChangedItem = (item) => ({
type: REMOVE_CHANGED_ITEM,
item,
})
selectedItemReducer.js
import {
ADD_ITEM, CLEAR_ITEMS, REMOVE_ITEM,
} from '../Constants'
const selectedItemReducer = (state = [], action) => {
switch (action.type) {
case ADD_ITEM:
return [
...state,
action.item,
]
case REMOVE_ITEM:
return state.filter((item) => item !== action.item)
case CLEAR_ITEMS:
return []
default:
return state
}
}
export default selectedItemReducer
changedItemReducer.js
import {
ADD_CHANGED_ITEM, REMOVE_CHANGED_ITEM,
} from '../Constants'
const changedItemReducer = (state = [], action) => {
switch (action.type) {
case ADD_CHANGED_ITEM:
return [
...state,
action.item,
]
case REMOVE_CHANGED_ITEM:
return state.filter((item) => item !== action.item)
default:
return state
}
}
export default changedItemReducer

If changedItems is guaranteed to always be an array, you should return those values in the reducer. From the looks of your changedItemReducer this appears to be the case.
const selectedItemReducer = (state = [], action) => {
switch (action.type) {
case ADD_ITEM:
return action.item;
case REMOVE_ITEM:
return state.filter((item) => item !== action.item)
case CLEAR_ITEMS:
return []
default:
return state
}
}
If not always an array then check first. If an array, return it, if not an array, place item into an array and return it.
const selectedItemReducer = (state = [], action) => {
switch (action.type) {
case ADD_ITEM:
return Array.isArray(action.item) ? action.item : [item];
case REMOVE_ITEM:
return state.filter((item) => item !== action.item)
case CLEAR_ITEMS:
return []
default:
return state
}
}
In both cases you are "throwing away" the old state when saving (returning) the new "changed items".

Related

How to prevent react context from adding duplicate item to cart

I am using react context to add item to the cart and I want to prevent duplicate items being added in the cart, I have tried tweaking the code, but it seems like something is missed. Below is the code sample:
const cartReducer = (cartState, action) => {
switch (action.type) {
case "add-cart": {
const item = action.payload;
const existItem = cartState.cart.find((x) => x.id === item.id);
if (existItem) {
return {
cart: [...cartState.cart, action.payload],
};
} else
return {
...cartState,
cart: [...cartState.cart, action.payload],
};
}
}
};
export const CartProvider = (props) => {
const [cartState, dispatch] = useReducer(cartReducer, {
cart: [],
});
function addToCart(val) {
dispatch({
type: "add-cart",
payload: val,
});
}
Why are you insert your item even if item already exists in the cart ?
You don't have to put your action.payload in the cart if already exists
if (existItem) {
return { ...cartState };
} else {
return {
...cartState,
cart: [...cartState.cart, action.payload],
};
}

How to update an array using redux reducer, I want to change a boolean property inside an array of objects to make i true or false

I have an array, every time I fire an action it adds a new item in the array with the value true and or false, I need to change that code to return the real number of the array not adding new items
Here is my code,
import {ads} from '../../data/ads';
import {ADD_TO_FAVORITE} from '../types';
interface ActionInter {
type: string;
payload: {id: number};
}
const initialState = {
allAds: ads,
myFavorite: [],
};
const myFavorite = (state = initialState, action: ActionInter) => {
switch (action.type) {
case ADD_TO_FAVORITE:
const itemFav = state.allAds[action.payload.id - 1].isFav;
console.log(itemFav);
if (itemFav === true)
return {
...state,
allAds: [
...state.allAds,
(state.allAds[action.payload.id - 1].isFav = false),
],
};
if (itemFav === false)
return {
...state,
allAds: [
...state.allAds,
(state.allAds[action.payload.id - 1].isFav = true),
],
};
}
return state;
};
export default myFavorite;
ads, it's an array of objects
You can using map like this:
const myFavorite = (state = initialState, action: ActionInter) => {
switch (action.type) {
case ADD_TO_FAVORITE:
return {
...state,
allAds: state.allAds.map((item, index) => {
return action.payload.id - 1 === index
? {
...item,
isFav: !item.isFav,
}
: item;
}),
};
}
return state;
};
You can try this:
const myFavorite = (state = initialState, action: ActionInter) => {
switch (action.type) {
case ADD_TO_FAVORITE:
const index = action.payload.id - 1;
const newAllAds = [...state.allAds];
newAllAds[index].isFav = !newAllAds[index].isFav;
return {
...state,
allAds: newAllAds,
};
}
return state;
};
You have to use immutability, instead of updating the previous state, create a copy and update that copy
const myFavorite = (state = initialState, action: ActionInter) => {
switch (action.type) {
case ADD_TO_FAVORITE: {
// Using the spread operator we'll create a copy
const allAdsCopy = [...state.allAds];
// Identify the item index
const index = action.payload.id -1;
// Update the copy
allAdsCopy[index].isFav = !allAdsCopy[index].isFav;
return {
...state,
allAds : allAdsCopy,
};
}
default:
return state;
}
};

How to implement Reducer when store initial state is an Array?

I'm trying to understand React- Redux basics, but I'm stuck in this particular case:
My code actions:
let todoId = 1
export const ADDTODO = 'AddTodo'
export const REMOVETODO = 'RemoveTodo'
export const TOINPROGRESS = 'ToInProgress'
export const TODONE = 'ToDone'
export function addTodo(payload){
return{
type: ADDTODO,
payload:{
status: 'Todo',
id: todoId++,
title: payload.title,
date:payload.date,
description:payload.description,
place:payload.place
}
}
}
export function removeTodo(todoId){
return{
type: REMOVETODO,
payload:todoId
}
}
export function toInProgress(todoId){
return{
type: TOINPROGRESS,
payload:todoId
}
}
export function toDone(todoId){
return{
type: TODONE,
payload:todoId
}
}
My attempt to reduce code:
import { addTodo, removeTodo, toInProgress, toDone } from '../actions';
const initialState = [];
const todos = (state = initialState, action) => {
switch(action.type) {
case 'AddTodo':
return[
...state, {
date:action.payload.date,
description:action.payload.description,
id:action.payload.id,
place:action.payload.place,
status:action.payload.status,
title:action.payload.title,
}
]
case 'RemoveTodo':
console.log(state)
return {
...state,
todos: state.todos.filter(todo => todo.id !== action.id)
}
case 'ToInProgress':
state.map(todo =>(todo.id===action.id)?{...todo,status:"InProgress"}:todo)
case 'ToDone':
state.map(todo =>(todo.id===action.id)?{...todo,status:"Done"}:todo)
default:
return state
}
}
The only working method from todos reducer is AddTodo, can't figure out to RemoveTodo, ToInProgress & ToDo to work.
I'm getting a TypeError at RemoveTodo that says "Cannot read property 'filter' of undefined"
and undefined returns from the another two methods.
In your case state is an array, so state.todos will be undefined. You can fix 'RemoveTodo' case with something like
case 'RemoveTodo':
return state.filter(todo => todo.id !== action.payload)
You missed return in other cases and you don't need to assign each property out of action.payload just pass it as is, this is how it would look
const todos = (state = initialState, action) => {
switch (action.type) {
case "AddTodo":
return [...state, action.payload];
case "RemoveTodo":
return state.filter((todo) => todo.id !== action.payload.id);
case "ToInProgress":
return state.map((todo) =>
todo.id === action.payload.id
? { ...todo, status: "InProgress" }
: todo
);
case "ToDone":
return state.map((todo) =>
todo.id === action.payload.id ? { ...todo, status: "Done" } : todo
);
default:
return state;
}
};

add key and value in array in redux

I am trying to update an array in reducer
let initialState = {
count: 0,
todos: [],
id:0,
}
const authReducer = (prevState = initialState, action) => {
switch (action.type) {
case types.ADD_TO_DO:
console.log(action.todo)
return {
...prevState,
todos: prevState.todos.concat(action.todo)
}
default:
return prevState;
}
}
And I am getting array in the form
todos:['qwerty', 'abcdef']
But I want in the form of
todos:[{id:'1', todo:'qwerty'},{id:'2',todo:'abcdef'}]
How can I achieve this?
Thanks!!!
In order to convert todos:['qwerty', 'abcdef'] to your expected format, you can map it:
var todos=['qwerty', 'abcdef'];
var result = todos.map((todo, i)=>({id:i+1, todo}));
console.log(result);
You can use reduce for this task
const todos = ['qwerty', 'abcdef']
const data = todos.reduce((acc, rec, index) => {
return [...acc, {
id: index + 1,
todo: rec
}]
}, [])
console.log(data)

React-Native Redux Create Array in Array

The main problem is about react-native redux/javascript in general.
I want to create a special kind of "Todo-list", which means, that there are parent categories (in this case PackingLists) and each of them includes a name, two dates and the item array.
That means:
We assume, that we have a list of "Packinglists" (e.g. Madrid, Paris and Berlin).
It should be possible to add items to each of them (e.g. Madrid contains a Jeans and Shirt and Paris a skirt & a pair of shoes). The idea behind the result should be clear now!
There is no problem in the creation of the Packinglists. I just donĀ“t know how to "append"/modify the "Packinglist" and add/remove items from the array in each list with react-redux (in PackingListReducer.js - the problem is in add_ItemToPackingList) . Can you help me?
index.js (actions)
export const addPackingList = (name, startDate, endDate, items) => ({
type: 'add_PackingList',
payload: {
name,
startDate,
endDate,
items
}
});
export const addItemToPackingList = (parentItem, itemName, isDone) => ({
type: 'add_ItemToPackingList',
payload: {
parentItem,
itemName,
isDone,
}
});
export const removePackingList = (name) => ({
type: 'remove_PackingList',
payload: name
});
PackingListReducer.js
export default (state = [], action) => {
switch (action.type) {
case 'add_PackingList':
return [...state, action.payload];
case 'add_ItemToPackingList': {
const currentList =
state.filter(packingList => packingList.name === action.payload.parentItem);
console.log(state);
console.log(currentList);
//return { ...state, items: [...currentList, action.payload] };
return Object.assign(state, { items: [...currentList, action.payload] });
}
case 'remove_PackingList':
return state.filter(packinglist => packinglist.name !== action.payload);
default:
return state;
}
};
Here I create the parent "PackingList", which works fine.
<TouchableOpacity
onPress={() => {
const exists = this.props.packingList.includes(
this.props.packingList.find(item => item.name === this.state.text));
console.log(endDate);
if (!exists) {
this.props.addPackingList(this.state.text, startDate, endDate, []);
this.props.navigation.goBack();
}
}
}
>
<Text style={styles.buttonStyle}>
CREATE NEW LIST!
</Text>
</TouchableOpacity>
</View>
The creation of the "item", which should be added to the parent packing list.
<TextInput
style={styles.inputText}
onSubmitEditing={() => {
this.props.addItemToPackingList(this.props.route.params.name,
this.state.text, false);
}}
</TextInput>
Try this:
Edited
// PackingListReducer.js
const currentListIndex =
state.findIndex(packingList => packingList.name === action.payload.parentItem);
let currentList = state[currentListIndex];
currentList = { ...currentList, items: [...currentList.items, action.payload] };
const newState = [...state];
newState[currentListIndex] = currentList;
...
return newState;
Updated
To delete the same items, reducer case would look like this (given the fact that you send itemName to your reducer in a payload):
case 'remove_ItemFromPackingList': {
const currentListIndex =
state.findIndex(packingList => packingList.name === action.payload.parentItem);
let currentList = state[currentListIndex];
currentList = { ...currentList, items: currentList.items.filter(item => item.itemName !== action.payload.itemName) };
const newState = [...state];
newState[currentListIndex] = currentList;
return newState;
}

Categories

Resources