Search code examples
javascriptreactjsreduxredux-toolkit

Issue with Dispatching Redux Actions in React Component


I encountered an issue while using Redux in my React component. Initially, I was trying to dispatch an action and then immediately dispatch another one afterwards.

Here's the original code snippet:

const onClickBtn = () => {
  dispatch(addProductStockAndPrices({
    productId: itemId,
    stockData,
  }));
  dispatch(getProductById(itemId));
}

However, this approach didn't work as expected. Later, I tried a different approach and it resolved the issue:

const onClickBtn = () => {
  dispatch(addProductStockAndPrices({
    productId: itemId,
    stockData,
  }))
    .finally(() => {
      console.log("finished addProductStockAndPrices");
      dispatch(getProductById(itemId));
    });
}

Can someone explain why the first approach didn't work while the second one did? I'm using React, Zod, React-Hook-Form, and Redux-Toolkit. Any insights would be appreciated.


Solution

  • Since you tagged I will assume that the addProductStockAndPrices action is an asynchronous action since it appears to return a Promise, e.g. createAsyncThunk.

    First Example:

    const onClickBtn = () => {
        dispatch(
            addProductStockAndPrices({
                productId: itemId,
                stockData,
            })
        );
        dispatch(getProductById(itemId));
    }
    

    In the first example you are dispatching two actions synchronously, i.e. one right after the other, and when the first action's asynchronous logic completes is irrelevant to the second action, the callback function didn't wait for it to complete before dispatching the second action.

    Second Example:

    const onClickBtn = () => {
        dispatch(
            addProductStockAndPrices({
                productId: itemId,
                stockData,
            })
        ).finally(() => {
            console.log("finished addProductStockAndPrices");
            dispatch(getProductById(itemId));
        });
    }
    

    The second example's code works because the finally block of a Promise chain runs at the end of the chain, after all promises in the chain have either resolved or rejected. In other words, the first action's asynchronous logic had to have completed and resolved or rejected prior to the second action being dispatched.

    Suggestion for code improvement

    All Redux-Toolkit Thunks will actually always resolve, so if the success of the first action matters at all to the second you will want to unwrap the result of the first. See Handling Thunk Results for details.

    • Using Promise chain

      const onClickBtn = () => {
        const payload = { productId: itemId, stockData };
      
        dispatch(addProductStockAndPrices(payload))
          .unwrap()
          .then((result) => {
            // addProductStockAndPrices success
            dispatch(getProductById(itemId));
          })
          .catch(error => {
            // addProductStockAndPrices failure
          });
      }
      
    • Using async/await + try/catch

      const onClickBtn = async () => {
        const payload = { productId: itemId, stockData };
      
        try {
          const result = await dispatch(addProductStockAndPrices(payload).unwrap();
      
          // addProductStockAndPrices success
          dispatch(getProductById(itemId));
        } catch(error) {
          // addProductStockAndPrices failure
        }
      }