React redux map reducer - reactjs

I have looked everywhere but nothing is working. I am trying to increment/decrement position based on which one they select on screen.
I am using useSelector to try and get a slice of the code.
const leagueSettings = useSelector((state) => state.drafts.rosterSettings);
But I cannot get the mapping to work. The state never updates. I can see that I am sending in an updated count for the position and the correct position.
My action.js
export const ADD_ROSTER_SPOT = "ADD_ROSTER_SPOT";
export const REMOVE_ROSTER_SPOT = "REMOVE_ROSTER_SPOT";
export const addRosterSpot = (position, count) => {
return {
type: ADD_ROSTER_SPOT,
position: position,
amount: count
};
};
export const removeRosterSpot = (position, count) => {
return {
type: REMOVE_ROSTER_SPOT,
position: position,
amount: count
};
};
my reducer.js
import { ADD_ROSTER_SPOT, REMOVE_ROSTER_SPOT } from "../actions/draft";
let initialState = {
leagueSettings: [], // list of league settings,
rosterSettings: [
{ Position: "QB", amount: 1 },
{ Position: "WR", amount: 1 },
{ Position: "RB", amount: 1 },
{ Position: "TE", amount: 1 },
],
drafted: [], // list of players drafted by others
};
export default (state = initialState, action) => {
switch (action.type) {
case ADD_ROSTER_SPOT:
return state.rosterSettings.map(pos => {
if (pos.Position === action.position) {
return {...pos, amount: pos.amount + 1}
}
console.log(state)
return pos;
});
case REMOVE_ROSTER_SPOT:
return state.rosterSettings.map((pos) => {
if (pos.Position === action.position) {
return { ...pos, amount: pos.amount - 1 };
}
return pos;
});
default: return state;
}
};

You need to change the code inside the reducer because it is not updating the state correctly
try using this in the "ADD_ROSTER_SPOT" case in reducer
let filteredState = state.rosterSettings.filter((eachPos) => {
if (eachPos.Position === action.Position) {
eachPos.amount = eachPos.amount + 1;
return eachPos;
}
return eachPos;
});
let newState = Object.assign({}, state, { rosterSettings: filteredState });
return({ ...newState });

Please check where the drafts reducer is defined, and what is your code in rootReducer?

Related

react redux thunk not populating state object

im having an issue with my code, its not populating the state object when state action is being performed. im new with redux
i have this code. so far that having an issue
this is the statement that will called the props.action fetchProjectFamilyList
case 'SubBusinessUnit':
setProductFamilyDetailsObj([])
if (selectedOption.id != 0) {
props.actions.fetchDepartment(selectedOption.id)
props.actions.fetchProjectFamilyList(selectedOption.id)
console.log(props)
}
setDropdownDataInState(resetData, 'Department')
setFormFields({
...formFields,
'OtherNamedInsuredIndustry': {
...formFields.OtherNamedInsuredIndustry,
value: ''
},
'NamedInsuredIndustry': {
...formFields.NamedInsuredIndustry,
value: "",
selectedId: 0
},
[fieldName]: {
...formFields[fieldName],
value: selectedOption.description, selectedId: selectedOption.id
}
});
break;
and this is the code for the commonreducer
export const fetchProjectFamilyList = createAsyncThunk(types.FETCH_PROJECT_FAMILY_LIST,
async (option, {getState, rejectWithValue}) => {
const reduxThunkConfig = {
checkStateData:getState().commonReducer.projectFamilyList && getState().commonReducer.projectFamilyList[option],
rejectWithValue
}
const APIConfig = {
URL: "eapi-referencedata/v1/lists/12?filterBySourceList=" + option + "&filterBySourceListValue=15",
method:"getData",
}
console.log('fetchProjectFamilyList')
return fetchCachedData(reduxThunkConfig, APIConfig);
}
)
im using the builder in my case of course inistailstate is set
const initialState = {
projectFamilyList:{},
}
builder.addCase(fetchProjectFamilyList.fulfilled, (state, action) => {
const subDivision = action.meta.arg;
return {
...state,
projectFamilyList:{
...state.projectFamilyList,
[subDivision]: action.payload},
}})
const commonActions = { ...actions, fetchProjectFamilyList }
export { commonActions, commonReducer}
this is the comment that accept the state as props. but the props productFamilyDetailsObj is empty object
<ProductFamilyComponent
productFamilyDetailsObj={productFamilyDetailsObj}
/>
function ProductFamilyComponent({ productFamilyDetailsObj }) {
return <div className="boxLayout">
<p className="smallHeading">Product Families</p>
{productFamilyDetailsObj.map((text, textIndex) => {
let index = textIndex;
return ( .... and so on
I hope theres someone who could help me resolving this. thank in advance.

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

So, I am making this shopping cart in redux-toolkit, where user can't order more than the quantity available. So, my reducer function is like below for adding to cart.
import { createSlice } from "#reduxjs/toolkit";
import { FoodCartType } from "../../types";
type CartState = {
cart: FoodCartType[];
};
const initialState: CartState = {
cart: [],
};
export const foodcartSlice = createSlice({
name: "foodcart",
initialState,
reducers: {
addToCart: (state, action) => {
console.log(action);
if (state.cart.length === 0) {
state.cart.push(action.payload);
}
const itemIndex = state.cart.findIndex(
(item) => item.id === action.payload.id
);
if (itemIndex >= 0) {
return {
...state,
cart: state.cart.map((item) => {
if (item.id === action.payload.id) {
return {
...item,
quantity: item.quantity + 1,
quantity_available: item.quantity_available - 1,
};
}
}),
};
} else {
return {
...state,
cart: [
...state.cart,
{
...action.payload.product,
quantity: 1,
quantity_available: action.payload.quantity_available - 1,
},
],
};
}
},
},
});
export const { addToCart } = foodcartSlice.actions;
export default foodcartSlice.reducer;
When I click on the dispatch, on the component. I get the following error.
Unhandled Runtime Error Error: [Immer] An immer producer returned a
new value and modified its draft. Either return a new value or
modify the draft.
So, where I am doing the problem? if I just remove the itemIndex check part, items get pushed into the cart. But, other than that it is giving me this error. How should I rewrite this?
Because your state.cart.push(action.payload); line modifies the value of state and later you return a new value.
You are allowed to do one, but not both at once. See writing reducers with immer
What you could do here: always modify. That's also far more readable.
export const foodcartSlice = createSlice({
name: "foodcart",
initialState,
reducers: {
addToCart: (state, action) => {
console.log(action);
// you push later anyways, so you can also delete these lines
// if (state.cart.length === 0) {
// state.cart.push(action.payload);
// }
const itemIndex = state.cart.findIndex(
(item) => item.id === action.payload.id
);
if (itemIndex >= 0) {
state.cart[itemIndex].quantity += 1
state.cart[itemIndex].quantity_available -= 1
} else {
state.cart.push({
...action.payload.product,
quantity: 1,
quantity_available: action.payload.quantity_available - 1,
})
}
},
},
});

How to access the updated value in my store immediately after updating it?

I have a React application that is currently using Redux for state management.
What I am trying to achieve: Click a Buy Now button - dispatch a action that makes a request to the server to add the item (increment the cart item count based on server response), check the state to see if the cart item count is greater than 0 & do something if it is.
For some reason, I have to click the button twice in order for the cartItemCount to reflect 1?
My current implementation looks like the below (I have tried to pull out all the unrelated code due to the file being quite large):
CourseSpecificScreen.tsx
const mapStateToProps = (state: RootState) => {
return {
courseSpecificReducer: state.courseSpecificReducer,
authState: state.authReducer,
currencyState: state.currencyReducer,
cartReducer: state.cartReducer,
courseCategoriesState: state.courseCategoriesReducer,
};
};
const mapDispatchTopProps = (dispatch: Dispatch<AnyAction>) => {
return bindActionCreators(ActionCreators, dispatch);
};
const connector = connect(mapStateToProps, mapDispatchTopProps);
type CourseSpecificScreenNavigationProp = CompositeNavigationProp<
StackNavigationProp<ExploreRouteStackParamList, "CourseSpecificScreen">,
CompositeNavigationProp<
StackNavigationProp<AppRouteHeaderParamList>,
StackNavigationProp<AuthRouteStackParamList>
>
>;
type CourseSpecificScreenRouteProp = RouteProp<
ExploreRouteStackParamList,
"CourseSpecificScreen"
>;
type Props = PropsFromRedux & {
navigation: CourseSpecificScreenNavigationProp;
route: CourseSpecificScreenRouteProp;
};
type State = {
cartItemCount: number;
};
class CourseSpecificScreen extends Component<Props, State> {
pruchaseItem = async () => {
const {
courseSpecificReducer,
clearCartAndAddItem,
navigation,
cartReducer,
getCartItemCount,
} = this.props;
const paymentMethod = paymentMethodForDevice();
await clearCartAndAddItem(
paymentMethod,
courseSpecificReducer.productData.code as string,
1,
navigation
)
if(cartReducer.cartItemCount > 0) {
// do some stuff
}
};
render() {
return (
<Button
btnStyle={[this.getStyles().smallButtonBuyCourse]}
labelStyle={[this.getStyles().buttonStickyLabelStyle]}
label={translate(
productData.isBundle && productData.isBundle === true
? "CategorySpecificScreen_buyThisBundle"
: "CategorySpecificScreen_buyThisCourse",
)}
onPress={this.purchaseItem}
disabled={false}
/>
)
};
CourseSpecificScreen.contextType = LocalizationContext;
export default connector(CourseSpecificScreen);
ThunkActions.ts
export const clearCartAndAddItem = (
paymentMethod: string,
productCode: string,
quantity: number,
navigation: any,
): AppThunk => {
return async (dispatch) => {
dispatch(cartActions.updateCartLoadingStatus(true));
const response = await cartServices.clearCart();
const {httpStatusCode} = response as APIResponse;
switch (httpStatusCode) {
case httpStatusCodes.SUCCESS_OK:
case httpStatusCodes.SUCCESS_CREATED:
case httpStatusCodes.SUCCESS_NO_CONTENT:
dispatch(cartActions.updateCartLoadingStatus(false));
dispatch(cartActions.updateCartItemCount(0))
globalConfig.setCartItemCount(0);
dispatch(addItemToCart(paymentMethod, productCode, quantity, navigation));
break;
case httpStatusCodes.CLIENT_ERROR_UNAUTHORIZED:
case httpStatusCodes.SERVER_ERROR_INTERNAL_SERVER_ERROR:
dispatch(cartActions.updateCartLoadingStatus(false));
let alertMessage = "Error, please try again later.";
if (response?.message) alertMessage = response?.message;
Alert.alert("Alert", alertMessage, [
{
text: "Ok",
},
]);
break;
default: {
dispatch(cartActions.updateCartLoadingStatus(false));
}
}
};
};
export const addItemToCart = (
paymentMethod: string,
productCode: string,
quantity: number,
navigation: any,
): AppThunk => {
return async (dispatch) => {
dispatch(cartActions.updateCartLoadingStatus(true));
const response = await cartServices.addItemToCart(productCode, quantity, paymentMethod);
const {httpStatusCode, data, error, message} = response as APIResponse;
console.log('add_item_to_cart_response:', response);
switch (httpStatusCode) {
case httpStatusCodes.SUCCESS_OK:
case httpStatusCodes.SUCCESS_CREATED:
dispatch(cartActions.updateCartLoadingStatus(false));
dispatch(cartActions.updateCartItemCount(quantity));
globalConfig.setCartItemCount(quantity);
break;
case httpStatusCodes.CLIENT_ERROR_UNAUTHORIZED:
dispatch(cartActions.updateCartLoadingStatus(false));
break;
case httpStatusCodes.SERVER_ERROR_INTERNAL_SERVER_ERROR:
case httpStatusCodes.CLIENT_ERROR_BAD_REQUEST:
dispatch(cartActions.updateCartLoadingStatus(false));
Alert.alert("Alert", (message)? message : "Error, it looks like you already have access to this course.", [
{
text: "Ok",
},
]);
break;
default: {
dispatch(cartActions.updateCartLoadingStatus(false));
}
}
};
};
Reducers.ts
const initialState: CartInitialState = {
isLoading: true,
cartToken: "",
responseStatus: apiResponseStatuses.IDLE,
cartItemCount: 0,
isMessageVisible: false,
message: "",
};
export default function cartReducer(
state = initialState,
action: CartActionTypes,
): CartInitialState {
switch (action.type) {
case UPDATE_LOADING_STATUS:
return {
...state,
isLoading: action.isLoading,
};
case UPDATE_CART_TOKEN:
return {
...state,
cartToken: action.cartToken,
};
case UPDATE_RESPONSE_STATUS:
return {
...state,
responseStatus: action.responseStatus,
};
case UPDATE_CART_ITEM_COUNT_TOKEN:
return {
...state,
cartItemCount: action.cartItemCount,
};
case CLEAR_DATA_ON_LOGOUT:
return {
...state,
isLoading: true,
cartToken: "",
responseStatus: apiResponseStatuses.IDLE,
cartItemCount: 0,
isMessageVisible: false,
message: "",
};
default: {
return state;
}
}
}
In the pruchaseItem() function of CourseSpecificScreen.tsx, I would like to dispatch a action that adds the item to the cart and immediately afterwards check if the cartItemCount has been updated & if it has, do something... This functionality works as expected, but only after clicking the Buy Now button twice.
I have ruled out the possibility of the issue being the API request failing the first time.
I have been stuck on this issue for several days now so any help or advice would be greatly appreciated. Let me know if I need to include more information
In my case, I was storing a reference of the old cartReducer state before it was being updated.
I got this working by updating my purchaseItem() function to look like the below:
pruchaseItem = async () => {
const {
courseSpecificReducer,
clearCartAndAddItem,
navigation
} = this.props;
const paymentMethod = paymentMethodForDevice();
await clearCartAndAddItem(
paymentMethod,
courseSpecificReducer.productData.code as string,
1,
navigation
)
const { cartReducer } = this.props;
if(cartReducer.cartItemCount > 0) {
// do some stuff
}
};

Reducer doesn't update state

I am trying to create a reducer to update a property in object, but i cant be able to update and store the new state information
Reducer
export default function hideCardNumber(state = INITIAL_STATE, action: Action) {
if (action.type === 'HIDE_CARDNUMBER') {
return {
...state,
data: {...state.data, action }}
}
else
return state
}
Action
export const toggleViewNumberCard = (cardId: number, hideCardNumber: boolean) => {
return {
type: 'HIDE_CARDNUMBER',
cardId,
hideCardNumber,
}
}
dispatch to action
function handleToggleViewCardNumber() {
cards.map((card: Card) => {
if (card.cardId === props.activeCard ) {
dispatch(toggleViewNumberCard(
card.cardId,
!card.hideCardNumber,
))
}
})
}
Initial State
export const INITIAL_STATE = {
activeCard: 0,
data: [
{
cardId: 0,
cardName: 'Card',
cardUsername: 'Name',
cardNumber: '1234 1234 1234 1234',
hideCardNumber: false, <-- Trying to replace this property when reducer update
},
]
}
You need to update reducer like this:
const {hideCardNumber, cardId} = action;
return {
...state,
data: state.data.map(item => item.cardId === cardId ? {...item, hideCardNumber} : item )
}
In real-world scenarios, cardID will be a hash. Also to easily maintain the store data when the application grows INITIAL_STATE should be like this.
export const INITIAL_STATE = {
activeCard: 0,
data: {
123456: {
cardId: 123456,
cardName: 'Card',
cardUsername: 'Name',
cardNumber: '1234 1234 1234 1234',
hideCardNumber: false,
},
}
}
Then the Reducer will be like this.
export default function hideCardNumber(state = INITIAL_STATE, action) {
if (action.type === 'HIDE_CARDNUMBER') {
return {
...state,
data: {
...state.data,
[action.cardId]: {
...state.data[action.cardId],
hideCardNumber: action.hideCardNumber
}
}
}
}
else
return state
}
If the activeCard matches one of the IDs in the cards, the code will work perfectly.
first of all it's better to put you data in payload like:
export const toggleViewNumberCard = (cardId: number, hideCardNumber: boolean) => {
return {
type: 'HIDE_CARDNUMBER',
payload : {
cardId,
hideCardNumber,
}
}
}
looks like you have array of cards ,first of all you must have find your current card that you wanna replace using cardid like this:
const index = state.data.findIndex(
(card) => cardId === action.payload.cardId
);
then copy your old array :
const newArray = [...state.data];
then replace that index of newarray with your new hideCardNumber value like this:
newArray[index] = {
...newArray[index],
action.payload.hideCardNumber
};
return {
...state,
data: newArray,
};
i hope it would help you

Saving value on Action or Reducer?

Im trying to have my players to lauch dices and then save values.
Right now whats happening is that the values always reset so only 1 and 0 are generated.
On a working example i would like to see the values being incremented like in a game, and see a player win 5-2 or 6-0 for example.
import { GENERATE_NUMBER } from './types';
import generateDiceNumber from '../util/DiceNumber';
export const rollDice = () => {
return dispatch => {
const result = [generateDiceNumber(), generateDiceNumber()];
let playerWins = 0;
let cpuWins = 0;
if (result[0] > result[1]) {
playerWins++;
} else if (result[0] < result[1]) {
cpuWins++;
}
return dispatch({
type: GENERATE_NUMBER,
payload: {
result,
playerWins,
cpuWins
}
});
};
};
//missing error validation ofc
This is my action
import { GENERATE_NUMBER, PLAYER_RESULT, CPU_RESULT } from '../actions/types';
const initialState = {
number: [],
playerWins: 0,
cpuWins: 0
};
export default function(state = initialState, action) {
switch (action.type) {
case GENERATE_NUMBER:
return {
...state,
number: action.payload.result,
playerWins: action.payload.playerWins,
cpuWins: action.payload.cpuWins
};
default:
return state;
}
}
I want to do this without messing up my reducer or the reducer structure. Whats the best way to do this please?
You are replacing the value on your store, you need to increment it. Try this:
return {
...state,
number: action.payload.result,
playerWins: state.playerWins + action.payload.playerWins,
cpuWins: state.cpuWins + action.payload.cpuWins
};

Resources