首页 > 解决方案 > 带有 vuex 和 axios 的 Vue.js - 只能在第二次加载时获取数据

问题描述

我创建了一个 Vue.js 应用程序,其中包含一个带有 vuex 的中央存储和一些使用 axios 的基本 API 调用来将数据提取到存储中。

我创建了以下商店操作:

  loadConstituencyByAreaCodeAndParliament({commit}, {parliament_id, area_code}) {
    axios.get('/cc-api/area-code/' + parliament_id + '/' + area_code)
         .then((response) => {
           commit('SET_CONSTITUENCY', response.data);
         })
         .catch(function(error){
           commit('SET_CONSTITUENCY', null);
          }
         )
  }

在单个组件文件中,我定义了用户输入区号的表单。然后,此表单调用此操作以获取符合区号的选区:

export default {
  name: 'AreaCodeForm',
  components: {
    PostalCodeInput
  },
  props: ['parliament_id'],
  data: () => ({
    postalCode: ''
  }),
  methods: {
    search_area_code(submitEvent) {
      let area_code = submitEvent.target.elements.area_code.value;
      let payload = {
        parliament_id: this.parliament_id,
        area_code
      }
      this.$store.dispatch('loadConstituencyByAreaCodeAndParliament', payload).
          then(() => {
        let constituency = this.$store.getters.getConstituency();
        // do some things with the data received from the API
        // but everything depending on constituency does not work the first time.
        // Data received from the API is here available only from the second time on
        // wehen this code run.
      })
    }
  }
}

我发现该$store.dispatch方法返回了一个承诺,但constituency变量仍然没有接收到通过loadConstituencyByAreaCodeAndParliament操作获取的数据,但仍然为空。我认为当我使用该promise.then方法时,数据应该已经存储在商店中,但事实并非如此。当我第二次输入区号时,一切正常。

标签: javascriptvue.jspromiseaxiosvuex

解决方案


return处理异步任务时,请始终记住该语句。您有两个选项来重构您的代码,保留promiseasync/await.

选项1:async/await


async loadConstituencyByAreaCodeAndParliament({ commit }, { parliament_id, area_code }) {
    try {
      const { data } = await axios('/cc-api/area-code/' + parliament_id + '/' + area_code)
      commit('SET_CONSTITUENCY', data)
      return data
    } catch (error) {
      commit('SET_CONSTITUENCY', null)
      return error
    }
  }

备注

  • return的两个块中的语句try/catch
  • .get在 axios 中是可选的,因为默认是get方法。
  • 您可以{ data }在默认情况下将对象解构赋值与 axios 一起使用。如果我没记错的话,默认的良好 http 响应会检索数据。甚至更复杂的方法可能是const { data: constituencyResponse } = await...您使用 constituencyResponse 并且每次可能节省 2 或 3 行代码。

选项 2:Promise


第一条路径:在商店里制作所有东西。

 // actions
loadConstituencyByAreaCodeAndParliament({ commit, dispatch }, { parliament_id, area_code }) {
  axios('/cc-api/area-code/' + parliament_id + '/' + area_code)
    .then(({data}) => {
      commit('SET_CONSTITUENCY', data)
      dispatch('actionTwo', constituency)
    })
    .catch((error) => {
      console.log("error", error)
      commit('SET_CONSTITUENCY', null)
    })
}

actionTwo({commit}, constituency) {
  console.log("actionTwo", constituency)
  // do something
  commit('COMMIT', 'Final value')
}
// Component
// You handle it with a computed property either referencing a getter or the store state.

{
  computed: {
    getConstituency(){
      return this.$store.state.constituency
    },
    getSomeOtherConstituency(){
      return this.$store.state.constituency.something / 3
    }
  },

  // Optionally if you want to listen and react to changes use a `watcher`.
  watch: {
    // Gets excecuted each time getConstituency updates.
    // ! Must have the same name.
    getConstituency(update) {
      // Do something, `update` is the new value.
    }
  }
}

第二条路径:处理组件内部的数据,然后更新存储。

Vue 组件

methods: {
 search_area_code(submitEvent) {
    const parliament_id = this.parliament_id
    const area_code = submitEvent.target.elements.area_code.value

    axios('/cc-api/area-code/' + parliament_id + '/' + area_code)
      .then(({data: constituency}) => {
          this.$store.commit('SET_CONSTITUENCY', constituency)
          // Do whatever you want with constituency now inside the component.
        })
      .catch((error) => {
        console.log("error", error)
        this.$store.commit('SET_CONSTITUENCY', null)
      })
  }
},

笔记:

$store.dispatch 方法返回一个承诺,但选区变量仍然没有接收到使用 loadConstituencyByAreaCodeAndParliament 操作获取的数据,但仍然为空。 当我第二次输入区号时,一切正常。

我认为这里的问题是您要么处理了错误的异步代码,要么尝试实现自定义模式来解决。

正如我之前所说,将 store getter 放在计算属性中,请查看Vuex-docs中的这个示例。

代码洞察:

// Your action doesn't return anything, you must `return axios.get` inside it.
this.$store.dispatch('loadConstituencyByAreaCodeAndParliament', payload).then(() => {
  let constituency = this.$store.getters.getConstituency()
})

// without the `return` statement the code above can be translated to
this.$store.dispatch('loadConstituencyByAreaCodeAndParliament', payload)
let constituency = this.$store.getters.getConstituency()

// If you opt for async a valid way would be
async doSomething(){
  await this.$store.dispatch('loadConstituencyByAreaCodeAndParliament', payload)
  let constituency = this.$store.getters.getConstituency()
}

// IF it still doesnt update anything try `$nextTick` https://vuejs.org/v2/api/

this.$nextTick(() => {
  this.data = this.$store.getters.getConstituency()     
})

我希望其中的一些内容有所帮助。


推荐阅读