add key and value in array in redux - javascript

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)

Related

Immer An immer producer returned a new value *and* modified its draft. Either return a new value *or* modify the draft

hi .
I am planning to create a shopping cart system for my site. I use React and Redux toolkit. But when I want to dispatch my states, I encounter this error. Thank you for helping me.
cartSlide (Cart Reducer) :
import {createSlice} from "#reduxjs/toolkit";
const initialState = {
selectedItems: [],
itemsCounter: 0,
total: 0,
checkout: false
}
const sumItems = items => {
const itemsCounter = items.reduce((total , product) => total + product.quantity, 0)
const totalPrice = items.reduce((total , product) => total + product.price * product.quantity,0).toFixed(2)
return {
itemsCounter,
totalPrice
}
}
export const cartSlice = createSlice({
name: 'cart',
initialState,
reducers: {
AddItem:(state,action)=>{
if (!state.selectedItems.find(item => item.id === action.payload.id)) {
state.selectedItems.push({
...action.payload,
quantity: 1
})
}
return {
...state,
selectedItems: [...state.selectedItems],
...sumItems(state.selectedItems),
checkout: false
}
},
RemoveItem: (state, action) => {
const newSelectedItems = state.selectedItems.filter(item => item.id !== action.payload.id);
return {
...state,
selectedItems: [...newSelectedItems],
...sumItems(newSelectedItems)
}
},
Increase: (state, action) => {
const indexI = state.selectedItems.findIndex(item => item.id === action.payload.id);
state.selectedItems[indexI].quantity++;
return {
...state,
...sumItems(state.selectedItems)
}
},
Decrease: (state, action) => {
const indexD = state.selectedItems.findIndex(item => item.id === action.payload.id);
state.selectedItems[indexD].quantity--;
return {
...state,
...sumItems(state.selectedItems)
}
},
Checkout: () => {
return {
selectedItems: [],
itemsCounter: 0,
total: 0,
checkout: true
}
},
Clear: () => {
return {
selectedItems: [],
itemsCounter: 0,
total: 0,
checkout: false
}
}
}
})
export const {AddItem,RemoveItem,Increase,Decrease,Checkout,Clear} = cartSlice.actions
export default cartSlice.reducer
The error is for the AddItem action, and when I delete the return part, the code works.
this part:
AddItem:(state,action)=>{
if (!state.selectedItems.find(item => item.id === action.payload.id)) {
state.selectedItems.push({
...action.payload,
quantity: 1
})
}
return {
...state,
selectedItems: [...state.selectedItems],
...sumItems(state.selectedItems),
checkout: false
}
},
Don't both modify the state object state.selectedItems.push and use a return.
Option A - Modify Draft
AddItem: (state,action) => {
// Check if item exists
if (!state.selectedItems.find(item => item.id === action.payload.id)) {
// add item since it did not exist.
state.selectedItems.push({
...action.payload,
quantity: 1
})
}
// Calculate new count and totals.
const sum = sumItems(state.selectedItems);
// Apply new count and totals to the state.
state.itemsCounter = sum.itemsCounter;
state.totalPrice = sum.totalPrice;
// Set checkout to false.
state.checkout = false;
}
Option B - New State
AddItem: (state,action) => {
// create a new array containing selected items.
let newItems = [
...state.selectedItems;
];
// check if item already exists
if (!newItems.find(item => item.id === action.payload.id)) {
// add item since it did not exist
newState.selectedItems.push({
...action.payload,
quantity: 1
});
}
// return a new state
return {
...state,
selectedItems: newItems,
...sumItems(newItems),
checkout: false
}
}
You should probably stick with modifying the draft method unless you intend to replace the state with a completely new state. Using the draft method Immer should handle the reference updates where needed and allows you to write code as if you were editting a normal mutable object (for the most part, see the docs for some limitations).

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;
}
};

creating a redux like stores using custom hooks

Here I implemented redux like store using custom hooks. everything goes well and code executed correctly but problem is that in reducer under switch statement "TOGGLE" there I return a updated state which is finally stored in globalstate but if I returned empty object {} instead of {products: updated} still globalstate updating correctly with a change that has been done in reducer...since i am not passing globalstate reference then how it is updated correctly
and what listeners exactly do in dispatch method in code
import MarkFavMulti from "./MarkFavMulti";
import classes from "./MarkFav.module.css";
import useStore from "../HookStore/Store";
import {reducer2} from "../SampleReducer";
const MarkFav = props => {
const [outfit, dispatch] = useStore(reducer2);
const onClicked = (id) => {
dispatch({type: "TOGGLE", id: id});
}
const element = outfit.products.map((item) => {
return <MarkFavMulti cloth={item.name}
favorite={item.favorite}
price={item.price}
key={item.id}
clicked={onClicked.bind(this, item.id)} />
});
return (
<main className={classes.Markfav}>
{element}
</main>
);
};
export default MarkFav;
import {useState, useEffect} from "react";
let globalState = {};
let listeners = [];
const useStore = (reducer) => {
const setState = useState(globalState)[1];
const dispatch = (action) => {
let curr = Object.assign({},globalState);
const newState = reducer({...curr}, action)
globalState = {...globalState,...newState};
for(let listener of listeners) {
listener(globalState);
}
};
useEffect(()=>{
listeners.push(setState);
return () => {
listeners.filter(item => item !==setState);
}
},[setState]);
return [globalState, dispatch];
};
export const initStore = (initialState) => {
if(initialState) {
globalState = {...globalState, ...initialState};
}
}
export default useStore;
let initialState = {
products: [
{ id: 1, name: "shirt", price: "$12", favorite: false },
{ id: 2, name: "jeans", price: "$42", favorite: false },
{ id: 3, name: "coat", price: "$55", favorite: false },
{ id: 4, name: "shoes", price: "$8", favorite: false },
]
}
const configureStore = () => {
initStore(initialState);
};
export default configureStore;
export const reducer2 = (state=initialState, action) => {
switch (action.type) {
case "TOGGLE":
let update = {...state};
let updated = [...update.products];
updated = updated.map(item => {
if(item.id === action.id) {
item.favorite = !item.favorite;
return item;
}
return item;
});
return {products: updated};
//if we return {} ...it will updated correctly in globalstate
default:
throw new Error("not reachable");
}
}
The behavior that you are describing is due to this object assignment right here:
item.favorite = !item.favorite;
Here you are directly mutating the properties of the item object. You probably thought that it would be fine since you are using a copy of the products array.
let update = {...state};
let updated = [...update.products];
What actually happens is that updated is a "shallow copy" of the original array. The array itself is a new array, but the items in that array are the same items as in the state. You can read more about that here.
You need to return a new item object instead of mutating it. Here's a concise way to write it using the ternary operator.
case "TOGGLE":
return {
...state, // not actually necessary since products is the only property
products: state.products.map((item) =>
item.id === action.id
? {
...item,
favorite: !item.favorite
}
: item
)
};

redux array not updating correctly

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".

How can i add objects into array using reducer

//i need to add objects into a array using reducer for contact book
//reducer
const addContact = (contacts = [], action) => {
let contactsArr = [{}];
if (action.type = "ADD_CONTACT") {
return [...contactsArr, action.payload];
}
return contacts;
};
actions
export const addContactRed = contact => {
return {
type: "ADD_CONTACT",
payload: contact
};
};
{
type:"ADD_CONTACT",
payload:{name:"xyz",phonenum:10101001}
}
{
type:"ADD_CONTACT",
payload:{name:"abc",phonenum:0101001}
}
//after dispatching two actions final array i want is
//contactsArr
[
{name:"xyz",phonenum:10101001},
{name:"abc",phonenum:0101001}
]
You don't have ton init let contactsArr = [{}]; It will reset the store value in your reducer. Just use the contact store variable
const addContact = (contacts = [], action) => {
// if (action.type = "ADD_CONTACT") {
if (action.type === "ADD_CONTACT") {
return [...contacts, action.payload];
}
return contacts;
};

Categories

Resources