Search code examples
javascriptreactjsreduxreact-reduxredux-toolkit

React Redux how can I fire an async fetch request to the backend after a dispatch changed the state?


I have a React Redux Toolkit slice where I want to:

  1. Update my state
  2. Fetch this updated state to my backend

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?


Solution

  • 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.