Yahya
Yahya

Reputation: 338

How to fetch, update and render data in Vue app using axios?

I'm trying to fetch data from the axios get request and then trying to update it from the response of another axios get request. However, I'm unable to render the data from the 2nd request.

The sample code extract is:

// api.js
  // fetch items - first api call
  fetchItems() {
    const ITEMS_ENDPOINT = `${ROOT_URL}/items`;
    return axios.get(ITEMS_ENDPOINT, config);
  },

  // fetch items info - 2nd api call
  fetchItemsInfo(itemId) {
    const ITEMS_INFO_ENDPOINT = `${ROOT_URL}/items/${itemId}`;
    return axios.get(ITEMS_INFO_ENDPOINT, config);
  },

// vue component
  methods: {
    async fetchItems() {
      const res = await api.fetchItems();
      this.items = res.data;
      console.log(this.items);
      // console output [{id:1, a:1}, {id:2, a:2}, {id:3, a:3}]
    },

    updateItems() {
      this.items.forEach(async (item) => {
          const itemId = item.id;
          const res = await api.fetchItemsInfo(itemId);
          const info = res.data;
          console.log(info);
          // console output {id:1, x:2}
          if (item.id === info.id) {
            item.x = info.x;
            console.log(item);
            // console output [{id:1, a:1, x:2}]
          }
      });
    },
  },

  async created() {
    await this.fetchItems();
    this.updateItems();
    console.log(this.items);
    // console output [{id:1, a:1, x:0}, {id:2, a:2, x:1}, {id:3, a:3, x:2}]
  },

// html
<ul>
      <li v-for="(item, index) in items" :key="index">
      <!-- out put is (only the data from 1st call is displayed): 
    1 || 1 ||
    2 || 2 ||
    3 || 3 ||
    -->
        {{ item.id }} || {{ item.a }} || {{ item.x }} 
      </li>
</ul>

Only the data from the 1st api call is being displayed, NOT from the 2nd call. I can log the data from the created hook and it displays as expected in the console. If I use a click (button click) method to call updateItems function then data renders as expected. However, I want the load it on page load.

I'm getting the same behavior even if I update the vuex state from the updateItems and render the state getters.

How can I render the data from the 2nd call as well?

many thanks

Upvotes: 2

Views: 857

Answers (2)

Yahya
Yahya

Reputation: 338

Based on the suggestion from Thakur Karthik, I could find a workaround. I changed forEach to for..of loop, created a new array in data, and finally copy the this.items to the new array. The updated part of the code is:

// .... 
    async updateItems() {
      for (const item of this.items) {
        const itemId = item.id;
        const res = await api.fetchItemsInfo(itemId);
        const info = res.data;
        console.log(info);
        // console output {id:1, x:2}

        if (item.id === info.id) {
          item.x = info.x;
          console.log(item);
          // console output [{id:1, a:1, x:2}]
        }
        // create a new array and copy items to it
        this.updatedItems = this.items;
      }

The second solution that I could find out is to call Vuex store action to update the state of items instead of copying. The above code will be:

    async updateItems() {
     
        //.... rest of the code

        // call a vuex store action (`setItems`) to update the state items
        this.setItems(this.items)
      }

Upvotes: 0

Josh Pospisil
Josh Pospisil

Reputation: 215

The problem is that you're adding new properties to a reactive item. This doesn't work. I think this should work for you b/c you are re-creating the entire object instead of adding new properties

updateItems() {
  this.items = this.items.map(async item => {
    const { data: info } = await api.fetchItemsInfo(item.id);
    return {
      ...item,
      ...info
    }
  });
},

If you know the property names you could also use Vue.set if you wanted

Vue.set(item, 'property1', info.property1)
Vue.set(item, 'property2', info.property2)
etc...

I think this will do what you asked, but I also think just adding the details to the response of the fetch items call would be far less expensive than making an additional request per item.

Upvotes: 2

Related Questions