I am trying to remove single item from cart in reducer but not it does not seems to work. itemsInCart is Updated in ADD_TO_CART but not in REMOVE_FROM_CART.
Can anyone suggest edit to my code....
I tried passing mutable/immutable params to manageItemCount()
function manageItemCount(allItems, newItem){
let itemIndex = [];
if(allItems.length > 0) {
allItems.forEach((elem, i) => {
if (elem.product.id == newItem.product.id) {
itemIndex.push(i);
};
});
if(itemIndex.length){
allItems.splice(itemIndex.length-1, 1);
}
}
return allItems;
}
let alreadyRemovedFromCart = false;
const cartReducer = (state = {
itemsInCart: []
}, action) => {
switch (action.type) {
case 'ADD_TO_CART':
state = {
...state,
itemsInCart: [...state.itemsInCart, action.payload]
};
break;
case 'REMOVE_FROM_CART':
state = {
...state,
itemsInCart: manageItemCount(...state.itemsInCart, action.payload)
};
break;
}
return state;
}
export default cartReducer;
manageItemCount accepts two parameters but you are spreading all the itemsInCart array. So it should be:
case 'REMOVE_FROM_CART':
state = {
...state,
itemsInCart: manageItemCount(state.itemsInCart, action.payload)
};
break
Also manageItemCount seems like it is doing just .filter on itemsInCart.
Related
My issue is when Im' trying to toggle boolean value within my useReducer function, by doing this it is causing the issue of the value changing back to the original:
function reducerBalls(state: any, action: any) {
let newState;
let item;
switch (action.type) {
case ACTIONS.INIT:
return action.balls;
case ACTIONS.SELECTED:
newState = [...state];
item = newState[action.index] ;
item.active = !item.active;
return newState;
default:
return state;
}}
Here is the dispatch event
function ballCheckboxHandler(ball: lotteryBalls, event: any) {
if(event.target.checked) {
return dispatch({type: ACTIONS.SELECTED, index: ball.number});
}
if(event.target.checked === false) {
return dispatch({type: ACTIONS.UNSELECTED, index: ball.number});
}
}
Now I know react.StrictMode is causing this and live mode they say this issue wont happen, but the problem comes down to the development of it.
You are shallow cloning the array, but then you mutate the actual object, so the item itself doesn't re-render.
Clone the object newState[action.index] by using spread, and change the active property:
function reducerBalls(state: any, action: any) {
switch (action.type) {
case ACTIONS.INIT:
return action.balls;
case ACTIONS.SELECTED:
const newState = [...state];
newState[action.index] = {
...newState[action.index],
active: !newState[action.index].active
};
return newState;
default:
return state;
}
}
I would also change the way that the action works to make it a bit simpler:
function reducerBalls(state: any, action: any) {
switch (action.type) {
case ACTIONS.INIT:
return action.balls;
case ACTIONS.SELECTED:
const newState = [...state];
newState[action.index] = {
...newState[action.index],
active: action.selected // use the selected value
};
return newState;
default:
return state;
}
}
function ballCheckboxHandler(ball: lotteryBalls, event: any) {
return dispatch({
type: ACTIONS.SELECTED,
index: ball.number,
selected: event.target.checked // selected is the checked state of the event
});
}
reducers.js
this is my initialState
const initialState = {
transfusions: [],
};
Here is my Switch logic
export default function (state = initialState, action) {
switch (action.type) {
case GET_TRANSFUSIONS:
return {
...state,
transfusions: action.payload,
};
case ADD_TRANSFUSION:
return {
...state,
transfusions: [...state.transfusions, action.payload],
};
case UPDATE_TRANSFUSION:
console.log(action.payload)
return {
...state,
transfusions: state.transfusions.map(
(transfusion) => transfusion.id === action.payload.id ? {
...state,
transfusion: action.payload
} : transfusions
),
};
case DELETE_TRANSFUSION:
return {
...state,
transfusions: state.transfusions.filter(
(transfusion) => transfusion.id !== action.payload
),
};
default:
return state;
}
}
I updated in the backend successfully but how do I update the frontend. I tried this method it works but it always needs to refresh the page to show me the updated data.
transfusions: state.transfusions.map( is relying on the old state so it is always one step behind
You can modify your code to something like this:
const transfusionExists = state.transfusions.find(t => t.id === action.payload.id)
if(!transfusionExists) state.transfusions.push(action.payload)
return {
...state
),
};
Finally I solved my issue. I used this code:
case UPDATE_TRANSFUSION:
const transfusionsExisted = state.transfusions.filter(
(transfusion) => transfusion.id !== action.payload.id
);
return {
transfusions: [action.payload, ...transfusionsExisted],
};
I deleted the record I wanted to update first and then pushed the new record.
I hope it will help you if you are suffering as I suffered from this problem.
I am currently working on an upvote/downvote feature and have shown state changes of how it on my reducers. I feel like the code in 'case types.ADD_VOTE' in my reducers file could be refactored to be cleaner.
I also included the container file as well to better understand what I am trying to achieve in the app.
Reducers
import * as types from '../constants/actionTypes.js';
/*
#giftList: List of objects
#lastGiftId:
*/
const initialState = {
giftList: [],
lastGiftId: 10000,
totalVotes: 0,
alreadyVoted: false,
newMessage: ''
};
const giftReducer = (state=initialState, action) => {
// let giftList;
// let setMessage;
switch(action.type) {
case types.ADD_GIFT:
let stateCopy = {...state};
stateCopy.lastMarketId += 1;
// create the new gift object structure.
const giftStructure = {
// lastGiftId: stateCopy.lastGiftId,
newMessage: stateCopy.newMessage,
totalVotes: 0
};
return {
...state,
lastMarketId: stateCopy.lastMarketId,
giftList: [...state.giftList, giftStructure],
newMessage: ''
}
case types.SET_MESSAGE:
return {
...state,
newMessage: action.payload,
}
case types.ADD_VOTE:
let stateCopy2 = {...state};
console.log("Already Voted Before: ", stateCopy2.alreadyVoted);
if(stateCopy2.alreadyVoted) {
stateCopy2.totalVotes -= 1;
stateCopy2.alreadyVoted = false;
} else {
stateCopy2.totalVotes += 1;
stateCopy2.alreadyVoted = true;
}
console.log("Already Voted after: ", stateCopy2.alreadyVoted);
return {
...state,
totalVotes: stateCopy2.totalVotes,
alreadyVoted: stateCopy2.alreadyVoted
}
default:
return state;
}
};
export default giftReducer;
List Container
const mapDispatchToProps = dispatch => ({
updateGiftMessage: (e) => {
console.log(e.target.value);
dispatch(actions.setMessage(e.target.value));
},
addGift: (e) => {
e.preventDefault();
console.log("actions: ", actions.addGift);
dispatch(actions.addGift());
},
addVote: (e) => {
e.preventDefault();
//console.log("event: ", e.target.getAttribute('mktid'));
dispatch(actions.addVote(e.target.getAttribute('gift-id')));
}
// }
});
class ListContainer extends Component {
constructor(props) {
super(props);
}
render() {
return(
<div className="All-Lists">
<h1>LIST CONTAINER!</h1>
<AllGiftsDisplay giftList = {this.props.giftList} addGift={this.props.addGift} setNewMessage={this.props.setNewMessage} totalVotes = {this.props.totalVotes} lastGiftId = {this.props.lastGiftId} addVote = {this.props.addVote} lastGiftId = {this.props.lastGiftId}/>
<GiftCreator setNewMessage={this.props.setNewMessage} updateGiftMessage={this.props.updateGiftMessage} addGift={this.props.addGift}/>
</div>
);
}
}
export default connect(mapStateToProps, mapDispatchToProps)(ListContainer);
If you don't need to log any intermediate state, or pre/post state:
case types.ADD_VOTE:
return {
...state,
totalVotes: state.totalVotes + (state.alreadyVoted ? -1 : 1),
alreadyVoted: !state.alreadyVoted,
};
Increment/Decrement total votes on value of already voted, and toggle the already voted value.
Using object destructuring, reduces code a little bit
case types.ADD_VOTE:
const { alreadyVoted, totalVotes } = state;
return {
...state,
totalVotes: totalVotes + (alreadyVoted ? -1 : 1),
alreadyVoted: !alreadyVoted,
};
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 }
}
I am using react/redux and have been looking at ways to update my array of posts in my reducer if a post is edited or deleted.
Is there a more simple way rather than doing something with creating an index function which finds the index of the post you are trying to delete/update, then returning the current state with the updated array using slice?
This is what I have so far in my reducer, I am kind of stuck:
import constants from '../constants';
const initialState = {
all: [],
sorted: [],
};
export default (state = initialState, action) => {
const newState = Object.assign({}, state);
switch (action.type) {
case constants.CREATE_OFFER:
newState[action.payload.id] = action.payload;
newState.all.unshift(action.payload);
return newState;
case constants.GET_OFFERS:
newState.all = action.payload;
return newState;
case constants.GET_OFFER:
newState[action.payload.id] = action.payload;
return newState;
case constants.EDIT_OFFER:
newState[action.payload.id] = action.payload;
return newState;
case constants.DELETE_OFFER:
newState[action.payload.id] = action.payload;
return newState;
case constants.SORT_OFFERS:
newState.sorted = action.payload;
return newState;
default:
return state;
}
};
Instead of :
newState[action.payload.id] = action.payload;
return newState;
You can use ES6 sytax :
return {...state, [action.payload.id]: action.payload.data};
i believe if you use Object as a app state rather than an array , it will be easy to update , delete all you need to do is use .mapKeys method of lodash library along with property you want to extract from your array (ideally 'id')
import _ from 'lodash';
return _.mapKeys(action.payload.data, 'id');
this will return :
{
'1': { id: 1,
title: 'Hello!',
},
'2': {
id: 2,
title: 'World'
}
}
You can read more Here
I think, everything is good, but it will be better to use es6 spread operator, your code will look more clear
case constants.CREATE_OFFER:
return {
...state,
all: state.all.concat(action.payload),
[action.payload.id]: action.payload
}
case constants.EDIT_OFFER:
return {
...state,
[action.payload.id]: action.payload,
all: state.all.map( item => item.id === action.payload.id?action.payload: item )
}
case constants.DELETE_OFFER:
const new = {
...state,
all: state.all.filter( item => item.id !== action.payload.id)
};
delete new[action.payload.id];
return new;
}