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,
}
},
},
});
Related
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
I have similar actions, If I select cart, cart should set to true and all other to false. Is there a way I can just write it in one action rather than separate actions for all of them?
I need something like "if payload == cart,true" then set cart to true and others to false.
const initialState = {
cart: false,
addNewAddress: false,
addNewCard: false,
changePassword: false,
};
export const overlaysSlice = createSlice({
name: "overlays",
initialState,
reducers: {
cart: (state, { payload }) => {
for (var key in state) {
state[key] = false;
}
state.cart = payload;
},
addNewAddress: (state, { payload }) => {
for (var key in state) {
state[key] = false;
}
state.addNewAddress = payload;
},
addNewCard: (state, { payload }) => {
for (var key in state) {
state[key] = false;
}
state.addNewCard = payload;
},
changePassword: (state, { payload }) => {
for (var key in state) {
state[key] = false;
}
state.changePassword = payload;
},
},
});
export const {
cart,
addNewAddress,
addNewCard,
changePassword,
} = overlaysSlice.actions;
export default overlaysSlice.reducer;
According to your question, payload is a string that could have values such as 'cart', 'addNewAddress', 'addNewCard', and 'changePassword'. If this is the case, you could use one action whose slice reducer function looks like this:
<action_name>: (state, { payload }) => {
return {
...initialState,
[ payload ]: true
};
}
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;
Before in Redux, I can pass payloads like:
export const quizSetState = (answerState, results) => {
return {
type: QUIZ_SET_STATE,
answerState,
results,
};
};
case QUIZ_SET_STATE:
return {
...state,
answerState: action.answerState,
results: action.results,
};
But how to do it in Redux-toolkit
dispatch(quizSetState({ [answerId]: 'success' }, results));
quizSetState: (state, action) => {
state.answerState = action.answerState;
state.results = action.results;
}
I can't handle this and instead dispatching 2 times
quizSetAnswerState: (state, action) => {
state.answerState = action.payload;
},
quizSetResultsState: (state, action) => {
state.results = action.payload;
},
dispatch(quizSetAnswerState({ [answerId]: 'success' }));
dispatch(quizSetResultsState(results));
PS: I read docs and there were preapre callback
action.payload can be an object that contains the variables you want to set, you can try this:
quizSetState: (state, action) => {
state.answerState = action.payload.answerState;
state.results = action.payload.results;
}
dispatch(quizSetState({answerState: { [answerId]: 'success' }, results})
Hope this helps!
I have started to create my application using #reduxjs/toolkit and kind of got stuck. I find no resource anywhere which can guide me with how to unit tests the logic in extraReducers. Any help would be appreciable.
Example:
Example:
const fetchList = createAsyncThunk('example/fetchList', async ({skip, reset, ...rest}) => {
const request = {
skip: reset ? initialState.skip : skip,
...rest,
};
return await getList(request);
});
const exampleSlice = createSlice({
name: 'example',
initialState: {id: '', list: []},
reducers: {
resetParams() {
return {id: '', list: []}
},
setId(state, {payload}) {
state.id = payload.id
}
},
extraReducers: {
[fetchList.pending]: (state) => {
state.fetching = true;
},
[fetchList.fulfilled]: (state, {payload = []}) => {
return {
fetching: false,
id: state.id + 1,
list: payload
}
},
[fetchList.rejected]: (state, {error}) => {
state.fetching = false;
},
},
});
//Tests .. for setId()
const initialState = {
id: 1,
list : []
}
const result = exampleSlice.reducer(initialState, exampleSlice.actions.setId({id: 10}))
expect(result.id).toEqual(10)
How can I test logic in extraReducers for fetchList.fulfilled and fetchList.rejected!
You can test it the same way as what you've shown.
Here's a simple way to just test the logic of the reducer based on the action types that createAsyncThunk outputs.
import reducer, {
fetchList
} from './exampleSlice';
describe('exampleSlice', () => {
describe('reducers', () => {
const initialState = { id: '', list: [], fetching: false }
it('sets fetching true when fetchList is pending', () => {
const action = { type: fetchList.pending.type };
const state = reducer(initialState, action);
expect(state).toEqual({ id: '', list: [], fetching: true });
});
it('sets the id and list when fetchList is fulfilled', () => {
const action = { type: fetchList.fulfilled.type, payload: { id: 1, list: [2, 3]} };
const state = reducer(initialState, action);
expect(state).toEqual({ id: 1, list: [2, 3], fetching: false });
});
it('sets fetching false when fetchList is rejected', () => {
const action = { type: fetchList.rejected.type, payload: { error: 'some error' } };
const state = reducer(initialState, action);
expect(state).toEqual({ id: '', list: [], fetching: false });
});
});
});
I also threw up a simple example of the same concept on a demo CSB: https://codesandbox.io/s/rtk-14-addmatcher-counter-test-l11mt?file=/src/features/counter/counterSlice.test.ts