Redux : update an item in an array of objects - javascript

In my reducer called 'reducer_product_list', I have this array :
let initialState = [
{name: 'Article 1', price: 5, quantity: 10},
{name: 'Article 2', price: 15, quantity: 8},
{name: 'Article 3', price: 7, quantity: 15},
{name: 'Article 4', price: 9, quantity: 5},
{name: 'Article 5', price: 11, quantity: 100},
{name: 'Article 6', price: 23, quantity: 20},
]
When I get the action 'ADD_TO_CART', I want to decrease the quantity of the selected object. The payload is one of those objects.
I typed the code above :
export default (state = initialState, action) => {
switch (action.type) {
case 'ADD_TO_CART':
initialState.map((product, i) => {
if (product.name === action.payload.name) {
initialState[i].quantity -= 1
return state;
}
});
default: return state
}
}
If I console.log my initialState, the quantity is decreasing, but in my container that renders the view, the quantity stays the same.
Thank you for your help.

Try this:
export default (state = initialState, action) => {
switch (action.type) {
case 'ADD_TO_CART':
return state.map(product => {
if (product.name === action.payload.name) {
return {...product, quantity: product.quantity-1}
};
return product;
});
default: return state
}
}
The reason is you have to return a new state object derived from the current state reflecting the desired changes according to the requested action. See Redux Reducers for more details.

Thank you for you help, it works well!
I just had to change one thing: instead of mapping on the initialState, I mapped on the state.
export default (state = initialState, action) => {
switch (action.type) {
case 'ADD_TO_CART':
return state.map(product => {
if (product.name === action.payload.name) {
return {...product, quantity: product.quantity - 1}
};
console.log(state)
return product;
});
default: return state
}
}

Related

React - Redux / Update Issue

The state for cart is following;
State> Cart> Products
{
'0': {
_id: '63c6e3d5f73ff2b0604b4e9c',
name: 'Oriental Rubber Table',
price: 521,
category: 'Pizza',
image: 'https://images.pexels.com/photos/461198/pexels-photo-461198.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1',
**amount: 1**
}
}
And I have this function to increase amount of the product. But It increases all the products in the basket not only the chosen item.
incrementAmount: (state, action) => {
return {products: state.products.map(product => {
if(product.id === action.payload){
return {...product, amount: product.amount + 1}
}else{
return product
}
})}
},

How To Update Redux State React Native

i have an object in redux state like this . the keys are actually the question ids and the values are answer ids.
userProgress: [
{
8: '2207',
12: '28',
38 : '42'
}
]
Now i want to send new values like
dispatch(setUserProgress({
12: '2800'
}))
it should find the value and update accordingly and if not found, it will add it at bottom.
Desired Result :
userProgress: [
{
8: '2207',
12: '2800',
38 : '42'
}
]
Assuming that your action is {type:'the type',payload:{12:'2800'}} then you can do the following:
return {...state, userProgress: {...state.userProgress,...payload}}
const reducer = (state, { type, payload }) => {
if (type === 'the type') {
return {
...state,
userProgress: { ...state.userProgress, ...payload },
};
}
return state;
};
console.log(
'add new answer at the end',
reducer(
{ userProgress: { a: 'a' } },
{ type: 'the type', payload: { newValue: 'newValue' } }
)
);
console.log(
'change existing value',
reducer(
{ userProgress: { b: 'b' } },
{ type: 'the type', payload: { b: 'newValue' } }
)
);

How to filter an array object in a reducer without affecting the actual state

I'm trying to filter an object from an array (redux reducer),
const data = [
{id: 0, name: 'Printed Men black Shirt', itemCode: 1000, price: 530, currency: '$', manufacturer: 'ColorWorld'},
{id: 1, name: 'Denim blue white shorts', itemCode: 1001, price: 230, currency: '$', manufacturer: 'ColorWorld'},
{id: 2, name: 'Solid Men blue pants', itemCode: 1003, price: 1530, currency: '$', manufacturer: 'Mecrono Hipli'},
{id: 3, name: 'Checkerd Men Blue Shorts', itemCode: 1300, price: 2530, currency: '$', manufacturer: 'Mecrono Hipli Mini'},
{id: 4, name: 'Self Designed Pant', itemCode: 1056, price: 130, currency: '$', manufacturer:
];
export const listProducts = (state = data, action) => {
switch(action.type){
case LIST_ALL_PRODUCTS: {
return state;
}
case SEARCH_PRODUCT: {
return state.filter((e)=> e.name.includes(action.payload))
}
default:
return state
}
}
This reducer always updates the actual state. Why this is not making a new filtered array?
As I mentioned in the comment, you can also separately store the filter itself and do the filtering not in the reducer, but in the component:
Example reducers:
export const listProducts = (state = data, action) => {
switch (action.type) {
default:
return state;
}
};
export const productNameFilter = (state = "", action) => {
switch (action.type) {
case SEARCH_PRODUCT: {
return action.payload.toLowerCase();
}
default:
return state;
}
};
// assuming root reducer is:
// combineReducers({ listProducts, productNameFilter })
In the productNameFilter reducer, the SEARCH_PRODUCT action's payload should contain the product name filter. Resetting the search filter is as simple as setting the state to ''.
Example component, using hooks:
const ProductList = () => {
const products = useSelector((state) => state.listProducts);
const productFilter = useSelector((state) => state.productNameFilter);
const shownProducts = products.filter((product) =>
product.name.toLowerCase().includes(productFilter.toLowerCase())
);
return (
<ul>
{shownProducts.map((p) => (
<p key={p.id}>{p.name}</p>
))}
</ul>
);
};
or with connect:
const ProductList = ({ products, productFilter }) => {
const shownProducts = products.filter((product) =>
product.name.toLowerCase().includes(productFilter)
);
return (
<ul>
{shownProducts.map((p) => (
<p key={p.id}>{p.name}</p>
))}
</ul>
);
};
export default connect(ProductList)((state) => ({
products: state.listProducts,
productFilter: state.productNameFilter,
}));
You can solve your issue by converting the state data structure to an object and maintain a separate property in redux state say filteredData.
Like this
const initialState = {
data: [
{
id: 0,
name: 'Printed Men black Shirt',
itemCode: 1000,
price: 530,
currency: '$',
manufacturer: 'ColorWorld',
},
{
id: 4,
name: 'Self Designed Pant',
itemCode: 1056,
price: 130,
currency: '$',
manufacturer: '',
},
],
filteredData: [],
}
export const listProducts = (state = initialState, action) => {
switch (action.type) {
case LIST_ALL_PRODUCTS: {
return state
}
case SEARCH_PRODUCT: {
return { ...state, filteredData: state.data.filter((e) => e.name.includes(action.payload)) }
}
default:
return state
}
}
Another option
Use single source of data in redux and do the filtering logic in the component itself. See sample demo here if it helps.

How can I filter an array which is in react redux reducer state?

This is my reducer file in react
const initialState = {
products: [
{
name: 'Icecream',
inCart: false,
num: 2
},
{
name: 'Cake',
inCart: true,
num: 5
}
]
};
export const reducer = (state = initialState, action) => {
switch (action.type) {
case REVERSE:
let newProd = [ ...state.products ];
newProd.filter((a) => {
if (a.inCart === true) {
return a;
}
});
console.log(newProd);
return {
...state,
products: newProd
};
default:
return state;
}
};
and this is the console log of newProd which shows the filter function doesn't work
0: {name: "Icecream", inCart: false, num: 2}
1: {name: "Cake", inCart: true, num: 5}
length: 2
__proto__: Array(0)
How can I filter the products array so I can only get the item that has inCart = true and replace it with the old products array ?
Filter returns a new array & won't mutate the original one. You are filtering but not assigning the output of filter to a variable.
Do this
export const reducer = (state = initialState, action) => {
switch (action.type) {
case REVERSE:
let newProd = state.products.filter((a) => { //<---- like this
if (a.inCart === true) {
return a;
}
});
console.log(newProd);
return {
...state,
products: newProd
};
default:
return state;
}
};
You can also do it in a single liner - like this:
...
case REVERSE:
return {
...state,
products: state.products.filter(a => a.inCart)
};
...

React-Redux: deleting an item does not re-render the array

I'm deleting invitations by passing their IDs to the back end, which works. However, my reducer is not working properly to re-render the new, filtered array of invitations. When I refresh the page, the deleted invitation is gone. What am I doing wrong?
The action:
export function deleteInvitation(id) {
const user = JSON.parse(localStorage.getItem('user'));
console.log('now deleting id ', id);
return function(dispatch) {
axios
.delete(`${ROOT_URL}/invitation/`, {
headers: { authorization: user.token },
params: { id: id }
})
.then(response => {
console.log(id);
dispatch({
type: DELETE_INVITATION,
id
});
});
};
}
The reducer:
export default function(state = {}, action) {
switch (action.type) {
case INVITATION_SUCCESS:
return { ...state, invited: true, error: {} };
case INVITATION_FAILURE:
return { ...state, invited: false, error: { invited: action.payload } };
case FETCH_INVITATIONS:
return { ...state, invitations: action.payload };
case DELETE_INVITATION:
return {
...state,
invitations: state.invitations.filter(_id => _id !== action.id)
};
default:
return state;
}
}
I'm making a guess about the structure of the invitations array...
In the reducer, the filter function appears to be incorrect. The action is passing an id property, which I'm guessing is a property of an invitation object. But the filter function is filtering objects from state.invitations where the object is the id. That is, the reducer is doing something like this:
const action = {id: 0}
const invitation = [{
_id: 0,
name: 'Name 0',
location: 'Location 0'
},
{
_id: 1,
name: 'Name 1',
location: 'Location 1'
},
{
_id: 2,
name: 'Name 2',
location: 'Location 2'
}
];
console.log(invitation.filter(_id => _id !== action.id));
which will return the full original array because the filter function is checking for the inequality of action.id (a number) to an invitation (an object). Basically:
{
_id: 0,
name: 'Name 0', !=== number
location: 'Location 0'
}
will return true for any num and/or any invitation object, so the filter function will return every item in state.invitations.
To correct this, check the invitation._id against the action.id, like this:
const action = {id: 0}
const invitation = [{
_id: 0,
name: 'Name 0',
location: 'Location 0'
},
{
_id: 1,
name: 'Name 1',
location: 'Location 1'
},
{
_id: 2,
name: 'Name 2',
location: 'Location 2'
}
];
console.log(invitation.filter(invitation => invitation._id !== action.id));

Categories

Resources