Как использовать Vuex со свойством асинхронного вычисляемого сеттера - PullRequest
0 голосов
/ 05 сентября 2018

У меня есть следующий компонент, где у меня есть вычисляемое свойство theme. Функция set вычисляемого свойства выполняет действие Vuex с именем setTheme, которое возвращает обещание, а также обновляет свойство theme в состоянии Vuex.

<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 голосов
/ 06 сентября 2018

Если я правильно понял, проблема, с которой вы столкнулись, заключается в том, что обе радиокнопки имеют проверенный эффект, который вызван 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>
...