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!
Related
I tried this but did not help me. this is my slice:
export const getAll = () => {
return axios.get('http://localhost:4000/students').then(response => {
return response.data
})
}
export const retriveStudents = createAsyncThunk(
"students/retrive", async () => {
return await getAll()
}
)
const studentSlice = createSlice({
name: "student",
initialState,
reducers: {
addNote: (state, action) => {
return { ...state, note: action.payload }
}
},
extraReducers: {
[retriveStudents.fulfilled]: (state, action) => {
studentSlice.actions.addNote("MY DATA...")
return { ...state, items: [...action.payload] }
}
}
})
In extraReducers I want to dispatch addNote action.
I tried the following, but does not work.
extraReducers: {
[retriveStudents.fulfilled]: (state, action) => {
studentSlice.actions.addNote("MY DATA...") // this. line ...
return { ...state, items: [...action.payload] }
}
}
I have an initial state like this.
const initialState = {
loading: false,
products: []
}
For the get products, I use thunk and I fetch datas from an API. loading field describes status of API call. If loading is true, i show progress in my component.
export const getProducts = createAsyncThunk('product/getProducts ',
async (payload, { rejectWithValue }) => {
try {
//fetch from somewhere
}
catch (e) {
return rejectWithValue(e.message)
}
}
)
const productSlice = createSlice({
name: "product",
initialState,
reducers: {
setLoading: (state, action) => {
state.loading = action.payload;
}
},
})
In a component first dispatch for loading and then getProducts.
// In some component
dispatch(setLoading(true))
dispatch(getProducts())
My question is, can I only call for getProducts and update loading state inside that function?
//In some component
dispatch(getProducts())
Yes you can. This is very simple
Update your createSlice section like this
const productSlice = createSlice({
name: "product",
initialState,
reducers: {},
extraReducers: (builder) => {
builder.addCase(getProducts.pending, (state) => {
state.loading = true;
});
builder.addCase(getProducts.fulfilled, (state) => {
state.loading = false;
});
},
})
You used createAsyncThunk and it is built in already to handle pending/fulfilled/rejected state with extraReducers in createSlice.
You can also handle the rejected state if fetch fail.
const productSlice = createSlice({
name: 'product',
initialState,
extraReducers: (builder) => {
builder.addCase(getProducts.pending, (state, action) => {
state.isLoading = true;
})
builder.addCase(getProducts.fulfilled, (state, action) => {
state.isLoading = true;
state.products = action.payload
})
builder.addCase(getProducts.rejected, (state, action) => {
state.isLoading = false;
})
},
})
To use it, will be enough to simple call dispatch(getProducts()) and extraReducers with thunk take care of the rest.
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;
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"
},
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,
}
},
},
});