Search code examples
reduxreact-reduxredux-thunkredux-async-actions

How to perform dependent async calls in redux?


Background

I am having react application which uses redux and redux-thunk.

Trying to understand how to orchestrate async calls. How to dispatch actions only if previously dispatched action (API call) was successul.

Use Case

I have EmployeesComponent. That component has two dummy presentational components

  • EmployeesList which basically lists employees.
  • AddEmployeeForm which is a modal for adding new employees.

When rendering the EmployeesComponent, within the useEffect hook, I dispatch an action to load employees. So far, so good.

When I user enters data in the AddEmployeeForm and submits the form, I want to achieve the load all employees again if the API call was successful.

I have issues understanding how to know if I am okay to dispatch the action to load all employees. Here is how my handler for submit looks like.

   const handleSubmit = async (e) => {
        e.preventDefault();
    
        console.log("Handle submit add new employee..");
    
        const employeeData = inputs;
        // Validate data from the AddEmployeeForm
        // in case of validation errors, abort



       dispatch(addEmployee(employeeData)); // calls API to save the data

       // How to await the result of the dispatched action (addEmployee) before continuing? 

       // If addEmployee was not successful abort
       // The error data from the API call is saved in the redux store, 
       // so the UI gets notified and can display it properly

        // If addEmployee was successful, I want to
        clearForm(); // local state from the component
        handleCloseForm(); // local state from the component

        dispatch(loadEmployees());
      };

Challenges

The current approach in handleSubmit smells to me. The business logic is kinda placed in the UI and the benefits of clearly separating the UI and business logic are lost. I have difficulties to figure out what is the approach to go and how to handle this use cases.

Another use case example:

  • dispatch login user
  • if login was successful, you get token from the backend
  • if login was successful, dispatch an action to load user data based on retrieved token

Solution

  • In Redux, async logic is normally written in the form of "thunk" functions. That lets you extract logic that needs to interact with the store outside of your React components, especially async logic that needs to do things like fetching data and dispatching the results.

    You can also return a promise from a thunk and await that promise in the component after dispatching.