Overflowing in state array using Redux Toolkit - reactjs

I got a situation where i add a box from an inputs and dispatched it when the user click save. But, the problem is when i add another box its values gets added to the old/existing array. So, for my Reducers as shown is there anything should be added or changed in order to achieve that every box should have its own skills.
import { createSlice } from "#reduxjs/toolkit";
const initialState = {
roles: [],
role: "",
description: "",
requiredSkill: [],
complimentarySkill: [],
requiredSkills: "",
complimentarySkills: "",
};
const rolesSlice = createSlice({
name: "teamRoles",
initialState,
reducers: {
addRole: (state, action) => {
if (!state.roles.includes(action.payload)) {
state.roles = [...new Set([...state.roles, action.payload])];
}
},
setRole: (state, action) => {
state.role = action.payload;
},
setDescription: (state, action) => {
state.description = action.payload;
},
addRequiredSkill: (state, action) => {
if (!state.requiredSkill.includes(action.payload)) {
state.requiredSkill = [
...new Set([...state.requiredSkill, { ...action.payload }]),
];
}
},
setRequiredSkill: (state, action) => {
state.requiredSkills = action.payload;
},
addComplimentarySkill: (state, action) => {
if (!state.complimentarySkill.includes(action.payload)) {
state.complimentarySkill = [
...new Set([...state.complimentarySkill, action.payload]),
];
}
},
setComplimentarySkill: (state, action) => {
state.complimentarySkills = action.payload;
},
},
});
export const {
addRole,
setRole,
setDescription,
addRequiredSkill,
setRequiredSkill,
addComplimentarySkill,
setComplimentarySkill,
} = rolesSlice.actions;
export default rolesSlice.reducer;
and for the code for the component when dispatched as shown here
{userRequiredSkills.length > 0
? userRequiredSkills.map((skill) => (
<div className="role__text-cont">
<p
key={skill.roleRequiredSkills}
className="role__text"
>
{skill.roleRequiredSkills}
</p>
</div>
))
: null}
what is being rendered on the page is as follows when one box is added and then when i add another box

Related

How to add an object to an array using redux toolkit

I want to add an object to an array reducer
This is the object am trying to add
const id = 1;
const type = 'deposit';
dispatch(addTransaction({id, type}))
This is my reducer
import { createSlice } from '#reduxjs/toolkit';
const initialState = {
transactions: [],
};
const transactionSlice = createSlice({
name: 'transaction',
initialState,
reducers: {
addTransaction: (state, action) => {
state.transactions = [...state.transactions, action.payload];
},
},
});
const { actions, reducer } = transactionSlice;
export const {
addTransaction,
} = actions;
export default reducer;
Anytime I dispatch the object data, It updates the previous data, instead adding a new object in my transaction array.
The problem is that you are not copying the transactions state before adding the new object to it.
so do this instead:
reducers: {
addTransaction: (state, action) => {
state.transactions = [...state.transactions, action.payload];
}
this way you copy all objects inside transactions array to a new array, plus the new object you want to add.
try :
change
const initialState = {
transactions: {
items: [],
},
};
to
const initialState = {
transactions:[]
};
{ state.transactions: [...transactions, action.transaction] };
or keep initial state but change reducer to:
state.transactions.items = [...state.transactions.items, action.payload];
and according to redux toolkit docs there is no return in reducer
Any of these two options should work:
Returning the updated state
const transactionSlice = createSlice({
name: 'transaction',
initialState,
reducers: {
addTransaction: (state, action) => {
return [...state.transactions, action.payload];
},
},
});
Or for future scalability, make your initial state to be an object and add a key for your transactions, in this case items
const initialState = {
transactions: {
items: [],
},
};
const transactionSlice = createSlice({
name: 'transaction',
initialState,
reducers: {
addTransaction: (state, action) => {
transactions.items = [...state.transactions.items, action.payload];
},
},
});
With this object approach you can take advantage of Immer and get rid of having to spread the state just pushing the incoming payload
const transactionSlice = createSlice({
name: 'transaction',
initialState,
reducers: {
addTransaction: (state, action) => {
transactions.items.push(action.payload);
},
},
});

React redux doesn't automatically render the changed things

I have a problem with React redux. And I want to display the current state immediately. Unfortunately this doesn't work as intended. The changed data is only displayed correctly after the page has been reloaded.
This is the Main Part in my Articel.js
const buySomething = async (articelId) => {
await axios.put(`/articel/request/${articelId}`).then((res) => {
dispatch(requested(currentUser._id));
setSnackbarMessage(res.data);
setOpen(true);
});
};
Articel model:
requested: {
type: [String],
default: [],
},
articelSlice.js
const initialState = {
currentArticel: null,
loading: false,
error: false,
};
requested: (state, action) => {
if (!state.currentArticel.requested.includes(action.payload)) {
state.currentArticel.requested.push(action.payload);
} else {
state.currentArticel.requested.splice(
state.currentArticel.requested.findIndex(
(userId) => userId === action.payload
),
1
);
}
},
Complete articelSlice Code:
import { createSlice } from "#reduxjs/toolkit";
const initialState = {
currentArticel: null,
loading: false,
error: false,
};
export const articelSlice = createSlice({
name: "articel",
initialState,
reducers: {
fetchStart: (state) => {
state.loading = true;
},
fetchSuccess: (state, action) => {
state.loading = false;
state.currentArticel = action.payload;
},
fetchFailure: (state) => {
state.loading = false;
state.error = true;
},
requested: (state, action) => {
if (!state.currentArticel.requested.includes(action.payload)) {
state.currentArticel.requested.push(action.payload);
} else {
state.currentArticel.requested.splice(
state.currentArticel.requested.findIndex(
(userId) => userId === action.payload
),
1
);
}
},
like: (state, action) => {
if (!state.currentArticel.likes.includes(action.payload)) {
state.currentArticel.likes.push(action.payload);
state.currentArticel.dislikes.splice(
state.currentArticel.dislikes.findIndex(
(userId) => userId === action.payload
),
1
);
}
},
dislike: (state, action) => {
if (!state.currentArticel.dislikes.includes(action.payload)) {
state.currentArticel.dislikes.push(action.payload);
state.currentArticel.likes.splice(
state.currentArticel.likes.findIndex(
(userId) => userId === action.payload
),
1
);
}
},
},
});
export const {
fetchStart,
fetchSuccess,
fetchFailure,
like,
dislike,
requested,
} = articelSlice.actions;
export default articelSlice.reducer;

React - Redux-Toolkit items in cart

I am learning react with redux-toolkit. I am stuck with some actions there.
I want to add quantity in Cart, so if I add same item more than once it should be like X1/x2/x3...etc.
And I want to delete items/item but only with the same ID ( when I click delete only delete that one ex. APPLE)
import { createSlice } from "#reduxjs/toolkit";
const initialState = {
hidden: true,
cartItems: 0,
itemsInCart: [],
quantity: 0,
totalCount: 0,
};
export const cartSlice = createSlice({
name: "cart",
initialState,
reducers: {
removeItem: (state, action) => {},
removeAll: (state) => {
state.cartItems = 0;
state.itemsInCart = [];
state.totalCount = 0;
},
addToCart(state, action) {
state.itemsInCar = state.itemsInCart.push(action.payload);
state.cartItems += 1;
state.totalCount += action.payload.price;
},
showCart: (state) => {
state.hidden = !state.hidden;
},
},
});
export const { showCart, addToCart, removeAll, removeItem } = cartSlice.actions;
export default cartSlice.reducer;
addToCart: (state, action) => {
const itemInCart = state.cart.find((item) => item.id === action.payload.id);
if (itemInCart) {
itemInCart.quantity++;
} else {
state.cart.push({ ...action.payload, quantity: 1 });
}
},

cannot update initalState in redux-toolkit with createAsyncThunk

Im trying to set and update initialState in redux toolkit after fetch operation
pageSlice.js
import { createSlice, createAsyncThunk } from "#reduxjs/toolkit";
import { deletePage, getPages, savePage, updatePage } from "../../services/page.service";
import { initialPage } from "../../components";
export const getUserPages = createAsyncThunk(
"pages/getUserPages",
async() => {
const pages = await getPages();
return pages
}
)
export const saveUserPage = createAsyncThunk(
"pages/saveUserPage",
async(page) => {
const savedPage = await savePage(page);
return savedPage;
}
)
export const pageSlice = createSlice({
name: "pages",
initialState: {
pageStatus: "idle",
pages: []
},
reducers: {},
extraReducers: {
[getUserPages.pending]: (state) => {
state.pageStatus = "loading";
},
[getUserPages.fulfilled]: (state, action) => {
state.pages = action.payload
state.pageStatus = "pageLoaded"
},
[getUserPages.rejected]: (state) => {
state.pageStatus = "error"
},
[saveUserPage.pending]: (state) => {
state.pageStatus = "loading";
},
[saveUserPage.fulfilled]: (state, action) => {
state.pages.push(action.payload)
state.pageStatus = "pageLoaded"
},
[saveUserPage.rejected]: (state) => {
state.pageStatus = "error"
}
}
})
export default pageSlice.reducer;
initialState: {
pageStatus: "idle",
pages: []
},
Working on note app with redux-toolkit. pages array will contain array of objects.
In extraReducers
[saveUserPage.fulfilled]: (state, action) => {
// console.log(state.pages) if empty( undefined ) else [{element}]
state.pages.push(action.payload)
state.pageStatus = "pageLoaded"
}
if initailState pages array contain any single element [saveUserPage.fulfilled] work fine.
but if array is empty then i get error
Cannot read property 'push' of undefined at pages/saveUserPage/fulfilled
if console.log(state.pages) in s
What I'm doing wrong ?
Based on the Error message you have mentioned Cannot read property 'push' of undefined at pages/saveUserPage/fulfilled, state.pages is not an empty array, It is undefined. That is the reason you are seeing this error.
Array.push operation, all it expects the variable to be array i.e. [], It doesn't matter whether it is empty or it has items.
Please check the below code block or some other operation, whether it is assigning it as 'undefined' in the first place.
[getUserPages.fulfilled]: (state, action) => {
state.pages = action.payload
state.pageStatus = "pageLoaded"
},
Workaround solution:
[saveUserPage.fulfilled]: (state, action) => {
if(state.pages === undefined){
state.pages = [];
if(action.payload && action.payload.length > 0){ / Make sure that payload is an array
state.pages = action.payload; //
}
}
else{
state.pages.push(action.payload);
}
state.pageStatus = "pageLoaded"
},

How to extract the id from the object payload?

For the articlesFetched action I'm getting an array payload like below, how can I extract/assign the id that is coming from the payload into the ids state field and for the state.data assign the whole object?
const payload =
[
{ id: 1,
items: 4
color: ['green', 'blue']
},
{ id: 2,
items: 10
color: ['yellow', 'red']
}
]
export const articlesSlice = createSlice({
name: 'articles',
initialState,
reducers: {
startFetching: (state) => {
state.loading = true
},
articlesFetched: (state, action) => {
state.loading = false
state.ids = ??
state.data = ??
},
},
});
If I'm reading things correctly, you should be able to do something like:
export const articlesSlice = createSlice({
name: 'articles',
initialState,
reducers: {
startFetching: (state) => {
state.loading = true
},
articlesFetched: (state, action) => {
state.loading = false
state.ids = action.payload.reduce((acc, item) => {
return [...acc, item.id]
}, [])
state.data = action.payload.reduce((acc, item) => {
return [...acc, ...item.color]
}, [])
},
},
});
Hey I think you're not correctly updating the state, you should not assign the values to the current state because you need to return a new copy of the updated state without mutating the original, you can accomplish what you want using a map function like this.
export const articlesSlice = createSlice({
name: 'articles',
initialState,
reducers: {
startFetching: (state) => {
return {
...state,
loading: true,
}
},
articlesFetched: (state, action) => {
// Array with the ids
const ids = action.payload.map(({id}) => id)
// The whole array with the objects
const data = action.payload
return {
...state,
loading: false,
ids,
data,
}
},
},
});

Resources