Search code examples
javascriptreactjsreact-reduxaxiosredux-thunk

Getting undefined when accessing array element even though it exists


I am sending a HTTP request using Axios inside an asynchronous action creator. The arguments I want to use to call the synchronous action creator are stored in an array. For some reason, when I try to console.log(args) (the array), I get the array displayed correctly, but when I try to console.log(args[0]) (the first element), I get undefined. I don't know what this issue is linked to. Here is my code snippet:

const initIngredients_Sync = (ingredientsData, error) => {
    console.log("sync args", ingredientsData, error);
    return({
        type: actionTypes.INIT_INGREDIENTS,
        initialState: ingredientsData,
        error
    });
};

export const initIngredients = () => {
    const args = [];
    return dispatch => {
        axiosInstance.get('/ingredients_prices.json')
            .then((response) => {
                const INGREDIENT_PRICES = response.data;
                
                const INGREDIENTS = (() => {
                    let ingredients = {};
                    for (const key in INGREDIENT_PRICES) {
                        if(key !== "base_price")
                        ingredients[key] = 0;
                    }
                    return ingredients;
                })();

                const ingredientsData = {
                    ingredients_prices: INGREDIENT_PRICES, 
                    ingredients: INGREDIENTS, 
                    totalPrice: INGREDIENT_PRICES.base_price
                };

                args.push(JSON.parse(JSON.stringify(ingredientsData)));
            })
            .catch((error) => {
                args.push(error);
            });

        console.log("args[0]", args[0]);

        args.length === 2
            ? dispatch(initIngredients_Sync(args[0], args[1])) // [ingredientsData, error]
            : dispatch(initIngredients_Sync(args[0], false)); // no error
    }
};

Output with console.log(args):

[]
   0: {ingredients_prices: {...}, ingredients: {...}, totalPrice: {...}}
   length: 1

Output with console.log(args[0]):

undefined

Solution

  • Axios is a promise based HTTP client for the browser and Node.js.

    So its methods like .get() will resolve asynchronously.

    An asynchronous model allows multiple things to happen at the same time. When you start an action, your program continues to run. When the action finishes, the program is informed and gets access to the result (for example, the data read from disk).

    Resource - https://eloquentjavascript.net/11_async.html

    That's why your console.log does not output the result as you expect. You can fix this by dispatching actions with args in then method.

    const initIngredients_Sync = (ingredientsData, error) => {
      console.log("sync args", ingredientsData, error);
      return {
        type: actionTypes.INIT_INGREDIENTS,
        initialState: ingredientsData,
        error,
      };
    };
    
    export const initIngredients = () => {
      const args = [];
      return (dispatch) => {
        axiosInstance
          .get("/ingredients_prices.json")
          .then((response) => {
            const INGREDIENT_PRICES = response.data;
    
            const INGREDIENTS = (() => {
              let ingredients = {};
              for (const key in INGREDIENT_PRICES) {
                if (key !== "base_price") ingredients[key] = 0;
              }
              return ingredients;
            })();
    
            const ingredientsData = {
              ingredients_prices: INGREDIENT_PRICES,
              ingredients: INGREDIENTS,
              totalPrice: INGREDIENT_PRICES.base_price,
            };
    
            args.push(JSON.parse(JSON.stringify(ingredientsData)));
    
            // These lines moved to `then` method
            console.log("args[0]", args[0]);
    
            args.length === 2
              ? dispatch(initIngredients_Sync(args[0], args[1])) 
              : dispatch(initIngredients_Sync(args[0], false));
          })
          .catch((error) => {
            args.push(error);
          });
      };
    };