Home > database >  Fetched data is one step behind after <select> value changes (Vue)
Fetched data is one step behind after <select> value changes (Vue)

Time:09-04

I'm trying to fetch data from a URL that changes each time a user selects a new value from a <select> dropdown. Each fetch populates the songkickData array with the new data. When I console.log the songkickData array on each <select> change, the data logged is from the previous selection. I want it to log the data from the current selection.

I think this has something to do with when the code is running / synchronicity / promises, but I haven't quite wrapped my head around that yet.

Here's my code:

<template>
  <select v-model="selected" @change="getCityData">
    <option v-for="city in cities" :key="city.id" :value="city.id">{{ city.label }}</option>
  </select>
</template>

import cityData from "../data/songkickCityData.js"

export default {
  data() {
    return {
      cities: cityData,
      songkickData: []
    }
  },
  methods: {
    getCityData(e) {

      const songkickCityId = e.target.value

      let songkickUrl = this.getSongkickUrl(songkickCityId)      

      fetch(songkickUrl)
        .then(res => res.json())
        
        .then(data => this.songkickData = data)
      
      this.getRandomGig()
      
    },
    getSongkickUrl(songkickCityId) {
      const now = new Date()
      const today = now.toISOString().slice(0, 10)
      const songkickAPIKey = 'XXXXXXXXXXXXX'
      let songkickUrl = `https://api.songkick.com/api/3.0/metro_areas/${songkickCityId}/calendar.json?min_date=${today}&apikey=${songkickAPIKey}`;

      return songkickUrl
    },
    getRandomGig() {
      
      // Here is where I want to get the data from current select value, not the previous one.
      console.log(this.songkickData)

    }
  }
}

CodePudding user response:

Here you are using then() instead of async/await. async/await pauses execution until the promise is resolved. The then() function will keep executing code and then return to perform your callback functions.

So your console.log() is running before your fetch is even resolved. The fetch then resolves, but the next time getCityData() runs it uses the old value because the new fetch has once again not resolved before the console.log().

Reference this article

In this case, you can do something like

<template>
  <select v-model="selected" @change="getCityData">
    <option v-for="city in cities" :key="city.id" :value="city.id">{{ city.label }}</option>
  </select>
</template>

import cityData from "../data/songkickCityData.js"

export default {
  data() {
    return {
      cities: cityData,
      songkickData: []
    }
  },
  methods: {
    getCityData(e) {

      const songkickCityId = e.target.value

      let songkickUrl = this.getSongkickUrl(songkickCityId)      

      fetch(songkickUrl)
        .then(res => res.json())
        
        .then(data => {
         this.songkickData = data
         this.getRandomGig()})
      
      
    },
    getSongkickUrl(songkickCityId) {
      const now = new Date()
      const today = now.toISOString().slice(0, 10)
      const songkickAPIKey = 'XXXXXXXXXXXXX'
      let songkickUrl = `https://api.songkick.com/api/3.0/metro_areas/${songkickCityId}/calendar.json?min_date=${today}&apikey=${songkickAPIKey}`;

      return songkickUrl
    },
    getRandomGig() {
      
      // Here is where I want to get the data from current select value, not the previous one.
      console.log(this.songkickData)

    }
  }
}

Or switch to using async/await

CodePudding user response:

Fetch API is an asynchronous web API and By default a fetch() request timeouts at the time setup by the browser. Hence, this.getRandomGig() method invoked before fetch() is resolved/rejected.

You can invoke this.getRandomGig() inside the promise retured by fetch to get the result from the current API response.

fetch(songkickUrl)
    .then(res => res.json())
    .then(data => { this.songkickData = data; this.getRandomGig(); })
  • Related