I have a React Redux Toolkit slice where I want to:
I would like to do something like this:
const sendStateToBackend = createAsyncThunk(
'.../sendStateToBackend',
async (state) => {
const data = await ...
}
)
createSlice({
...,
reducers: {
addTodo:(state, action) => {
state.todos.push(action.payload)
dispatch(sendStateToBackend(state)) // This is an Anti pattern???
}
},
extraReducers(builder) {
builder.addCase(sendStateToBackend.pending, (state) => {
state.isLoading = true
}
builder.addCase(sendStateToBackend.fulfilled, (state, action) => {
state.isLoading = false
}
}
})
In my page.tsx:
const onSubmit = async (values) => {
try {
dispatch(addTodo(values))
// Can I trigger another dispatch here?
} catch (error) {
console.error('An error occurred while submitting the form: ', error)
}
}
How can I achieve this?
you can use the extraReducers
section of your Redux Toolkit slice to handle the API call triggered by the addTodo
action. Instead of dispatching the sendStateToBackend
action inside the addTodo
reducer, you should let the extraReducers
handle it when the addTodo
action is fulfilled.
Here's how you can refactor your code:
1- Remove the dispatch from the addTodo
reducer in the slice:
import { createAsyncThunk, createSlice } from '@reduxjs/toolkit';
const sendStateToBackend = createAsyncThunk('.../sendStateToBackend', async (state) => {
// Implement your API call here and return the response data
// For example, you might use fetch or axios to send the state to the backend
// const response = await fetch('your-api-url', {
// method: 'POST',
// body: JSON.stringify(state),
// headers: {
// 'Content-Type': 'application/json',
// },
// });
// const data = await response.json();
// return data;
});
const todoSlice = createSlice({
name: 'todo',
initialState: { todos: [], isLoading: false },
reducers: {
addTodo: (state, action) => {
state.todos.push(action.payload);
},
},
extraReducers(builder) {
builder
.addCase(sendStateToBackend.pending, (state) => {
state.isLoading = true;
})
.addCase(sendStateToBackend.fulfilled, (state, action) => {
state.isLoading = false;
// Do something with the response data from the backend if needed
// const responseData = action.payload;
});
},
});
export const { addTodo } = todoSlice.actions;
export default todoSlice.reducer;
2- In your page.tsx, you can dispatch both actions sequentially inside the "onSubmit" function:
import { useDispatch } from 'react-redux';
import { addTodo, sendStateToBackend } from './your-todo-slice';
const YourPageComponent = () => {
const dispatch = useDispatch();
const onSubmit = async (values) => {
try {
// Dispatch the addTodo action to update the state
dispatch(addTodo(values));
// Dispatch the sendStateToBackend action to send the updated state to the backend
await dispatch(sendStateToBackend(values));
} catch (error) {
console.error('An error occurred while submitting the form: ', error);
}
};
return (
<div>
{/* Your page content */}
</div>
);
};
export default YourPageComponent;
when the addTodo
action is dispatched, it will update the state in the Redux store. Then, the extraReducers
section will handle the sendStateToBackend
action, making the API call to send the updated state to the backend when the addTodo
action is fulfilled. By using await
in the onSubmit
function, you can ensure that the API call is completed before continuing with the rest of the code.