如何将Vuex与异步计算的setter属性一起使用

时间:2018-09-05 10:45:15

标签: javascript vue.js async-await vue-component vuex

我具有以下组件,其中具有theme计算属性。计算属性的set函数执行一个称为setTheme的Vuex动作,该动作返回一个Promise,并在Vuex状态下更新theme属性。

<template>
  <div>
    <input id="light-theme-radio" v-model="theme" type="radio" value="light">
    <label for="light-theme-radio">Light</label>

    <input id="dark-theme-radio" v-model="theme" type="radio" value="dark">
    <label for="dark-theme-radio">Dark</label>
  </div>
</template>

<script>
import Vue from "vue";
import { createNamespacedHelpers } from "vuex";

const { mapActions } = createNamespacedHelpers("theme");

export default {
  computed: {
    theme: {
      get() {
        return this.$store.state.theme.theme;
      },
      set(value) {
        this.setTheme(value);
      }
    }
  },
  methods: {
    ...mapActions(["setTheme"])
  }
};
</script>

问题在于theme.get完成以新选择的项目更新单选按钮后,没有调用setTheme计算属性。使用异步设置器时解决此问题的最佳方法是什么?这就是我的Vuex的样子:

export const state = {
  theme: "light"
};

export const mutations = {
  theme: (s, p) => (s.theme = p)
};

export const actions: ActionTree = {
  async setTheme(context, theme) {
    context.commit("theme/theme", theme);
    // ...omitted
    await Timer.delay(750);
    // ...omitted
  }
};

const mainModule = {
  actions,
  getters,
  mutations,
  namespaced: true,
  state
};
export default mainModule;

const modules = {
  other: otherModule,
  theme: themeModule
};

const store = new Store({
  modules,
});
export default store;

1 个答案:

答案 0 :(得分:0)

如果我的理解正确,那么您遇到的问题是两个单选按钮均已被选中效果,这是由于Vue无法及时渲染所致。

因此解决方案是让Vue首先渲染,然后等待承诺。完成后,再次渲染。

下面是两种方法

  1. 使用vm.$forceUpdate

  2. 提交一个伪造的值,例如loading...,Vue将首先渲染(Vue是数据驱动的),在出现真实值之后,Vue将再次自动渲染。

下面是一个简单的演示:

Vue.config.productionTip = false
const store = new Vuex.Store({
  state: {
    theme: "light"
  },
  mutations: {
    theme: (s, p) => (s.theme = p)
  },
  actions: {
    setTheme: async function (context, theme) {
      return new Promise((resolve, reject) => {
        setTimeout(()=> {
          context.commit("theme", theme)
          resolve('done')
        }, 1500)
      })
    }
  }
})

new Vue({
  el: '#app',
  store,
  data() {
    return {
      updatedCount: 1
    }
  },
  computed: {
    theme: {
      get() {
        return this.$store.state.theme
      },
      set(value) {
        //or use this.$forceUpdate() instead
        this.$store.commit("theme", 'loading...') //or other values
        this.setTheme(value)
      }
    }
  },
  updated(){
    console.log('updated', this.updatedCount++)
  },
  methods: {
    ...Vuex.mapActions(["setTheme"])
  }
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
<script src="https://unpkg.com/vuex@3.0.1/dist/vuex.js"></script>
<div id="app">
  <div>
    <h3>{{theme}}</h3>
    <input id="light-theme-radio" v-model="theme" type="radio" value="light">
    <label for="light-theme-radio">Light</label>

    <input id="dark-theme-radio" v-model="theme" type="radio" value="dark">
    <label for="dark-theme-radio">Dark</label>
  </div>
</div>