在vuex中“模拟”突变

时间:2017-12-10 09:44:53

标签: vue.js vuejs2 vuex

import { remoteSettings } from 'somewhere';

const store = {
    state: {
        view: {
            foo: true
        }
    },
    mutations: {
        toggleFoo(state) {
            state.view.foo = !state.view.foo;
        }
    },
    actions: {
        async toggleFoo({ state, commit }) {
            commit('toggleFoo');
            await remoteSettings.save(state);
        }
    }
};

说我有一个像这样的简单商店。 toggleFoo操作应用突变,然后通过进行异步调用来保存新状态。但是,如果remoteSettings.save()调用失败,则我在商店中进行的本地设置和远程设置不同步。我真正想要在这个行动中实现的是这样的:

async toggleFoo({ state, commit }) {
    const newState = simulateCommit('toggleFoo');
    await remoteSettings.save(newState);
    commit('toggleFoo');
}

我想在不实际提交的情况下获得新状态。如果远程调用成功,那么我实际上会更新商店。如果没有,它会保持不变。

实现这一目标的最佳方法是什么(没有实际复制突变功能中的逻辑)?也许“撤消”?我不确定。

2 个答案:

答案 0 :(得分:4)

这样做的一种方法是:(信用 @Bert 以纠正错误)

  1. 在提交变异之前使用const oldState = state;存储旧状态。

  2. try-catch块中包装异步调用。

  3. 如果remoteSettings失败,它会将执行传递给catch阻止。

  4. 在catch块中提交一个突变来重置状态。

  5. 示例:

    const store = {
      state: {
        view: {
          foo: true
        }
      },
      mutations: {
        toggleFoo(state) {
          state.view.foo = !state.view.foo;
        },
        resetState(state, oldState){
          //state = oldState; do not do this
    
           //use store's instance method replaceState method to replace rootState
            //see :   https://vuex.vuejs.org/en/api.html
          this.replaceState(oldState)
        }
      },
      actions: {
        async toggleFoo({ state, commit }) {
          const oldState =  JSON.parse(JSON.stringify(state));  //making a deep copy of the state object
          commit('toggleFoo');
          try {
            await remoteSettings.save(newState);
            //commit('toggleFoo'); no need to call this since mutation already commited
          } catch(err) {
            //remoteSettings failed
            commit('resetState', oldState)
          }
        }
    
      }
    };
    

答案 1 :(得分:2)

借用@VamsiKrishna的代码(谢谢),我建议另类。在我看来,您希望将更改发送到服务器,并在成功时更新本地状态。这是一个有效的例子。

为了防止重复逻辑,将更改抽象为函数。



console.clear()

const remoteSettings = {
  save(state){
    return new Promise((resolve, reject) => setTimeout(() => reject("Server rejected the update!"), 1000))
  }
}

function updateFoo(state){
  state.view.foo = !state.view.foo
}

const store = new Vuex.Store({
  state: {
    view: {
      foo: true
    }
  },
  mutations: {
    toggleFoo(state) {
      updateFoo(state)
    },
  },
  actions: {
    async toggleFoo({ state, commit }) {
      // Make a copy of the state. This simply uses JSON stringify/parse
      // but any technique/library for deep copy will do. Honestly, I don't
      // think you would be sending the *entire* state, but rather only
      // what you want to change
      const oldState = JSON.parse(JSON.stringify(state))
      // update the copy
      updateFoo(oldState)
      try {
        // Attempt to save
        await remoteSettings.save(oldState);
        // Only commit locally if the server OKs the change
        commit('toggleFoo');
      } catch(err) {
        // Otherwise, notify the user the change wasn't allowed
        console.log("Notify the user in some way that the update failed", err)
      }
    }
  }
})

new Vue({
  el: "#app",
  store,
  computed:{
    foo(){
      return this.$store.state.view.foo
    }
  },
  mounted(){
    setTimeout(() => this.$store.dispatch("toggleFoo"), 1000)
  }
})

<script src="https://cdnjs.cloudflare.com/ajax/libs/vuex/3.0.1/vuex.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.9/vue.js"></script>
<div id="app">
  <h4>This value never changes, because the server rejects the change</h4>
  {{foo}}
</div>
&#13;
&#13;
&#13;