Utilize Vuex to retrieve information using axios by building on existing data that has already been fetched

Utilizing axios in the Vuex store, I am fetching data from an external API that returns an array of objects representing various cities.

Each city object includes a zip code, which is essential for retrieving detailed information about the city. My objective is to append this details-object as a new key within the cities array.

Currently, I employ one Vuex action to fetch all cities and another action specifically for fetching city details.

Action to retrieve all cities

fetchCities: ({ commit, state, dispatch }) => {
  let baseUrl = "https://my-url/cities";

  let config = {
    headers: {
      accept: "application/json",
      Authorization: ...
    },
    params: {
      param1: "a",
      param2: "b"
    }
  };

  axios
    .get(baseUrl, config)
    .then(function(response) {
      commit("UPDATE_CITIES_RAW", response.data.items);
    })
    .catch(function(error) {
      console.log(error);
    });
  dispatch("fetchCityDetails");
},

Mutation after fetching all cities

UPDATE_CITIES_RAW: (state, payload) => {
  state.citiesRaw = payload;
},

Every object in the array contains a zip code, necessary for obtaining city details.

I attempted iterating through the citiesRaw array within the action to fetch details, but encountered an issue where the array was empty at that point due to the action being called before the mutation.

Action to obtain city details

fetchCityDetails: ({ commit, state }) => {
  let baseUrl = "https://my-url/cities/";

  let config = {
    headers: {
      accept: "application/json",
      Authorization: ...
    };
  
  state.citiesRaw.forEach(e => {
    let url = baseUrl + e.zipCode;

    axios
      .get(url, config)
      .then(function(response) {
        commit("UPDATE_CITY_DETAILS", {
          response: response.data,
          zipCode: e.zipCode
        });
      })
      .catch(function(error) {
        console.log(error);
      });
  });
},

What are some optimal methods to wait for the initial fetch and then update the array? Is it advisable to manipulate the existing array or create a new one?

Alternatively, are there more effective ways to fetch data based on existing data within the Vuex store?

Update

Following corrections suggested by @Y-Gherbi regarding dispatch timing, I revamped the process of fetching city details:

  1. The component initiates the fetchCities dispatch
  2. In the fetchCities action: Commit UPDATE_CITIES
  3. In the UPDATE_CITIES mutation: Utilize .map on the payload to generate new objects and push them into state.cities
  4. In the fetchCities action: Iterate over state.cities and dispatch fetchCityDetails(zipCode) for each city
  5. In the fetchCityDetails action: Commit UPDATE_CITY_DETAILS
  6. In the UPDATE_CITY_DETAILS mutation: Implement .map on state.cities to incorporate cityDetails object for each city

New actions

fetchCities: ({ commit, state, dispatch }) => {
  let baseUrl = "https://my-url/cities";

  let config = {
    headers: {
      accept: "application/json",
      Authorization: ...
    },
    params: {
      param1: "a",
      param2: "b"
    }
  };

  let url = baseUrl;

  axios
    .get(url, config)
    .then(function(response) {
        commit("UPDATE_CITIES", response.data.items);
        state.cities.forEach(city => {
          dispatch("fetchCityDetails", city.zipCode);
        });
      }
    })
    .catch(function(error) {
      console.log(error);
    });
},
fetchCityDetails: ({ commit }, zipCode) => {
  let baseUrl = "https://my-url/cities";

  let config = {
    headers: {
      accept: "application/json",
      Authorization: ...
    },
  };

  let url = baseUrl + "/" + zipCode;

  axios
    .get(url, config)
    .then(function(response) {
      commit("UPDATE_CITY_DETAILS", {
        cityDetails: response.data,
        zipCode: zipCode
      });
    })
    .catch(function(error) {
      console.log(error);
    });
}

New mutations

UPDATE_CITIES: (state, cities) => {
  // Extracting required data & renaming keys from the response to create new objects
  cities = cities.map(city => {
    let obj = {};
    obj.zipCode = city.zip_code
    obj.key1 = city.key_1;
    obj.key2 = city.key_2;

    return obj;
  });
  state.cities.push(...cities);
},
UPDATE_CITY_DETAILS: (state, payload) => {
  let cities = state.cities;
  // Include a details-object for each city
  cities = cities.map(city => {
    if (city.zipCode == payload.zipCode) {
      city.cityDetails = payload.cityDetails;
    }
    return city;
  });
  state.cities = cities;
}

Exploring whether there exists a more efficient approach for handling this type of data retrieval.

Answer №1

There are definitely better and more elegant ways to handle this issue, but for now, let me assist you with the bug of looping over an empty array that should contain cities.

Resolution:

Consider moving the dispatch function to the line after the commit function. The array (citiesRaw) remains empty because the action is triggered before the data is fetched, as axios.get is an asynchronous operation.


Another approach:

You mentioned displaying cities in a table with expandable rows showing city details. While fetching a large number of cities may seem like a lot, it's one backend call. However, requesting details individually for each item could impact performance and bandwidth usage.

In my opinion, handling requests only when the user needs the data would be more efficient. For instance, fetching details when the user expands a row.

Storage in the state?

The decision to store city details in the state is yours to make. You can keep it in the component or use it as a form of caching.

Upon expanding a row, check if the details are already retrieved:

if(!state.cityDetails[key]) {
    axios.get()
        .then(res => {
            // Save fetched details through a mutation
        })
        .catch((error) => {
            // Handle errors
        })
} else {
    // Use cached cityDetails
}

A sample structure for your state:

{
    cityDetails: {
        keyCouldBeIdOrZipcode: {...},
        anOtherAlreadyFetchedCity: {...},
    }
}

Apologies for any mistakes in typing or formatting. Writing code on a phone can be challenging.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

What is the best way to transfer information from a vue-resource function to the visual layer of a vue component?

Within my resource.js file, I have an ES6 class that is exported: import Vue from 'vue' import VueResource from 'vue-resource' Vue.use(VueResource) export class Resource { getMovies () { // GET /someUrl return Vue.http.g ...

Unable to retrieve file from server

Apologies for the basic question, but I'm feeling a bit lost at the moment. I've been diving into learning JavaScript and decided to set up a page on the 00webhost site to experiment and run tests. Everything was going well until I hit a roadblo ...

The state variable remains undefined even after integrating useEffect in a React.js component

Hello, I have a component within my React application that looks like this: import React, { useEffect, useState } from "react"; import AsyncSelect from "react-select/async"; import { ColourOption, colourOptions } from "./docs/data"; const App = () => ...

Unable to retrieve the desired information

Being new to development, I recently attempted an AJAX search and encountered the following issues. Upon running it, I received an "undefined" error message. How can this be resolved? This is where you enter text. <input id="word" type="text" placehol ...

Ways to effectively access props in Vue.js without having to pass them through a div element

Currently, I have an API call being made in the create hook of my Vue root. After that call, a component is attempting to utilize the data returned from the API. My inquiry is straightforward. Is it possible to have <stage-execs></stage-execs> ...

Is there a way to execute a JavaScript file within another JavaScript file?

I'm in the process of developing a text-based game through the console, utilizing an NPM package known as "prompts" to prompt the user for input. One specific question it asks is, "What OS do you want to run?" and returns the selection as JSON data. F ...

What steps can be taken to address the issue of the body-parser module being disabled in a node

My API is not functioning properly and I have observed that the body-parser module seems to be disabled in some way. Despite my efforts, I have been unable to find any information on this issue. Please refer to the image attached below for further details. ...

What could be the reason behind getting a blank email?

Why am I receiving empty emails without any field values? HTML <div class="contact-form wow fadeIn" data-wow-duration="1000ms" data-wow-delay="600ms"> <div class="row"> <div class="col-sm-6"> <form id="main-contact-form" ...

Retrieve an array from the success function of a jQuery AJAX call

After successfully reading an rss file using the jQuery ajax function, I created the array function mycarousel_itemList in which I stored items by pushing them. However, when I tried to use this array in another function that I had created, I encountered t ...

loading client-side native addons

Can native add-ons (coded in C/C++ similar to nodejs) be loaded from client-side javascript using requireJS or other modules? I am currently developing a nodejs + express application that delivers an HTML page loading JavaScript files. I'm utilizing ...

React: Deep requiring has been phased out with the latest version of uuid. Make sure to only require the top-level

My React application is displaying the button successfully, but I am encountering an error. In my index.js file, I used const uuidv4 = require('uuid/v4');, which is now deprecated as of [email protected] Please ensure you require the top-le ...

Error in Typescript: Draggable function is undefined

I'm currently working with typescript alongside vue and jquery ui. Encountering the error "TypeError: item.$element.draggable is not a function". What am I doing wrong in my code? I have already included jquery-ui, as shown in the following files. M ...

Strategies for extracting data from a third-party website that utilizes JavaScript to set the value

Before, I would use jQuery to load external website content such as html or json. Sometimes, I even utilized a proxy PHP page in order to bypass strict origin policies on certain sites. However, I've encountered an issue with some websites. In the HT ...

"Troubleshooting: Why is the guildMemberAdd event in Discord.js not

I'm currently in the process of creating a Discord bot for my personal server using Discord.js and following the discord.js guide. However, I've run into an issue with my event handler. When I add a file for another event, the code within that m ...

Tips on stopping or unbinding a previous $watch in AngularJS

Currently, I am utilizing $watch in a dynamic manner which results in the creation of another $watch on each call. However, my intention is to unbind the previous $watch. $scope.pagination =function(){ $scope.$watch('currentPage + numPerPage', ...

Playing embedded YouTube videos automatically in Safari 11 without user interaction

I’m encountering an issue with a simple modal dialog: When a user clicks on a button, the modal overlay appears. An embedded YouTube <iframe> is then added. Everything works smoothly in most browsers, except for Safari 11.1. Safari’s new auto ...

Best Practices for Organizing js/app.js in Laravel and Vue Applications

After utilizing Vue components and a Bootstrap carousel component in my simplistic app, I encountered an issue. Upon compiling my assets with Laravel Mix, placing the compiled app.js between the head tags resulted in the Vue components failing to work (dis ...

Tips on how to reach an Angular component within a third-party library

I'm currently working on a web application that incorporates the Deezer player through the official JS SDK. I've run into an issue where I am unable to access my Angular component from within the Deezer event subscription. The arrow function does ...

Issue related to React Routing: "The component [AuthProvider] is being used incorrectly. All child components within <Routes> must be either a <Route> or <React.Fragment>."

Dealing with React Routing has been a challenge for me. I've integrated authentication using Firebase and it seems like I need to wrap my routes with the <AuthProvider>. However, I keep encountering an error stating that it's not a part of ...

At what specific times is it most appropriate to use parentheses when dealing with functions?

Check out my code snippet below: const cleanRoom = function() { return new Promise(function(resolve, reject) { resolve('Cleaned The Room'); }); }; const removeGarbage = function(message) { return new Promise(function(resolve, reject) ...