Redux Toolkit: dispatch an action in extraReducers of current slice - reactjs

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] }
}
}

Related

Redux toolkit how to call action from other slice on one action fullfilled

I have a async action name editLoginIdData() in loginsIdSlice.js,
which i am dispatching from certain component, which edits the data in mongoDB database, then when the action is fullfilled, i mutate the state in extraReducers
editLoginIdData.fulfilled.
But now what i want to do that whenever the editLoginIdData action is fullfilled
i want to also add the updated(which i will get from server responese -> updatedData at editLoginIdData()) data to activitiesData state, which i am handling in activitiesSlice.js
So basically is there a way that when editLoginIdData action is fullfilled we can
dispatch somehow mutate the state in other slice.
One approach i have taken is to import the editLoginIdData() action in activitiesSlice.js and then creating a extraReducer with editLoginIdData.fullfilled
and mutating activitiesData state.
I have done the above approach and seems its working correctly.
But there is a catch, like how show i get the response data at editLoginIdData()
to passed to activitiesSlice.js because i will required that updated data.
if the above appraoch is not correct, then how should i do it
loginsIdSlice.js
import { createAsyncThunk, createSlice } from "#reduxjs/toolkit";
import * as api from "../../api"
const initialState = {
loginsIdData: [],
}
export const fecthLoginIdsData = createAsyncThunk("loginIds/fetch", async ({ user_id }, { getState }) => {
const res = await api.fetchUserLoginIds(user_id);
console.log(res);
const { data } = res;
data.reverse();
return data;
});
export const addNewLoginIdData = createAsyncThunk("loginIds/add", async ({ data, user_id }, { getState }) => {
const res = await api.addNewLoginIdA(data, user_id)
const { loginIdsArray } = res.data;
return loginIdsArray[loginIdsArray.length - 1];
});
export const editLoginIdData = createAsyncThunk("loginIds/edit", async ({ updatedData, login_id }, { getState }) => {
const res = await api.editLoginId(login_id, updatedData);
// console.log(updatedData);
return updatedData;
});
export const deleteLoginData = createAsyncThunk("loginIds/delete", async ({ login_id, user_id }, { getState }) => {
const res = await api.deleteLoginId(login_id, user_id);
// console.log(res);
const { data } = res;
// console.log(data);
return data.reverse();
});
//* Slice
const loginsIdSlice = createSlice({
name: 'loginsId',
initialState: initialState,
extraReducers: (builder) => {
builder.
addCase(fecthLoginIdsData.fulfilled, (state, action) => {
return {
...state,
loginsIdData: action.payload
};
}).
addCase(addNewLoginIdData.fulfilled, (state, action) => {
return {
...state,
loginsIdData: [action.payload, ...state.loginsIdData]
};
}).
addCase(editLoginIdData.fulfilled, (state, action) => {
const newArray = state.loginsIdData.map((loginId) => {
if (loginId._id === action.payload._id) {
return action.payload;
} else {
return loginId;
}
});
return {
...state,
loginsIdData: newArray,
};
}).
addCase(deleteLoginData.fulfilled, (state, action) => {
return {
...state,
loginsIdData: action.payload
};
})
}
})
export const { deleteLoginId, editLoginId } = loginsIdSlice.actions;
export default loginsIdSlice.reducer;
activitiesSlice
import { createAsyncThunk, createSlice } from "#reduxjs/toolkit";
import * as api from "../../api"
import { editLoginIdData } from "../loginsId/loginsIdSlice"
const initialState = {
activitiesData: [],
}
const activitiesSlice = createSlice({
name: 'activities',
initialState: initialState,
extraReducers: (builder) => {
builder.
addCase(editLoginIdData.fullfilled, (state, action) => {
console.log("ss")
return {
...state,
activitiesData: []
};
})
}
})
export default activitiesSlice.reducer;
Is there a way that when editLoginIdData action is fullfilled we can
dispatch somehow mutate the state in other slice.

Redux toolkit update state in thunk

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.

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;

Redux toolkit can I pass several payloads to the action?

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!

createAsyncThunk action always rejected

Hi i have a problem with createAsyncThunk, i have started a json-server with json document in it.
i'm tryng to dispatch the action getTodos but the action is always rejected and i do not why.
i'm sure the server is working properly because i testes it with postman, any one can help me?
import { createAsyncThunk, createSlice } from "#reduxjs/toolkit";
import { filterTodo } from "./FilterSlice";
// the url are deleted for privacy
const urlTodos = "";
const urlFilter = "";
export const getTodos = createAsyncThunk(
"todos/getTodos",
async (data = null, thunkAPI) => {
const todosPromise = fetch(urlTodos)
.then((resp) => resp.json())
.then((res) => res);
console.log("todoPromise", typeof todosPromise);
const filterPromise = fetch(urlFilter)
.then((resp) => resp.json())
.then((res) => res);
let [todos, activeFilter] = await Promise.all([
todosPromise,
filterPromise,
]);
thunkAPI.dispatch(filterTodo(activeFilter));
return todos;
}
);
export const todosSlice = createSlice({
name: "todos", // nome della slice
initialState: [],
reducers: {
addTodo(state, action) {
console.log("add todo");
state.push(action.payload);
},
removeTodo(state, action) {
state = state.filter((todo) => todo.name !== action.payload.name);
return state;
},
toggleTodo(state, action) {
state = state.map(function (todo) {
if (todo.id === action.payload.id) {
todo.completed = !todo.completed;
}
});
},
},
extraReducers: (builder) => {
// reducer che gestisce la pending action
builder
.addCase(getTodos.pending, (state, actions) => {
state.status = "loading";
console.log("in pending");
})
.addCase(getTodos.fulfilled, (state, action) => {
state.status = "success";
console.log("in getTodos fulfilled", action.payload);
state = action.payload;
return state;
})
.addCase(getTodos.rejected, (state, action) => {
console.log("in getTodos rejected", action.payload);
});
},
});
const { actions, reducer } = todosSlice;
export const { addTodo, removeTodo, toggleTodo } = actions;
export default reducer;
it was dispatched in the following way
const dispatch = useDispatch();
useEffect(() => {
console.log("dentro use effect");
dispatch(getTodos());
return () => {};
}, [dispatch]);
and the structure of the json object is the following
{
"todos": [
{
"completed": true,
"name": "Call my mum",
"dueDate": "17/3/2022",
"user_id": 1,
"id": 1
},
{
"completed": false,
"name": "go to school",
"dueDate": "17/3/2022",
"user_id": 2,
"id": 2
},
{
"completed": true,
"name": "do homework",
"dueDate": "17/3/2022",
"user_id": 3,
"id": 3
}
],
"filter": ["ALL"]
}
this is what appen when i try to excute the program
the console is that
the redux extension of chrome reports this error
i found the error:
extraReducers: (builder) => {
builder
.addCase(getTodos.pending, (state, actions) => {
console.log("in pending");
})
.addCase(getTodos.fulfilled, (state, action) => {
console.log("in getTodos fulfilled", action.payload);
state = action.payload;
return state;
})
.addCase(getTodos.rejected, (state, action) => {
console.log("in getTodos rejected", action.payload);
});
},
});
i removed the state.status = string and now it works

Resources