简体   繁体   English

vuex中的“模拟”突变

[英]“Simulate” mutations in 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);
        }
    }
};

Say I have a simple store like this. 说我有一个像这样的简单商店。 toggleFoo action applies the mutation, then saves the new state by making an async call. toggleFoo操作将应用突变,然后通过进行异步调用来保存新状态。 However, if remoteSettings.save() call fails, local setting I have in the store and remote settings are out of sync. 但是,如果remoteSettings.save()调用失败,则存储中的本地设置与远程设置不同步。 What I really want to achieve in this action is something like this: 我在此操作中真正想要实现的是这样的:

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

I'd like to get the new state without actually committing it. 我想获得新状态而不实际提交。 If remote call succeeds, then I'll actually update the store. 如果远程调用成功,那么我将实际更新商店。 If not, it's going to stay as it is. 如果没有,它将保持原样。

What's the best way to achieve this (without actually duplicating the logic in the mutation function)? 实现此目的的最佳方法是什么(实际上不复制变异函数中的逻辑)? Maybe "undo"? 也许“撤消”? I'm not sure. 我不确定。

One way of doing this would be: (credit to @Bert for correcting mistakes) 一种实现方法是:(改正错误时@Bert致谢

  1. Store the old state using const oldState = state; 使用const oldState = state;存储旧const oldState = state; before committing the mutation. 进行突变之前。

  2. Wrap the async call in a try-catch block. 将异步调用包装在try-catch块中。

  3. If the remoteSettings fails it will pass the execution to catch block. 如果remoteSettings失败,它将把执行传递给catch块。

  4. In the catch block commit a mutation to reset the state. 在catch块中提交一个突变以重置状态。

Example: 例:

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)
      }
    }

  }
};

Borrowing code from @VamsiKrishna (thank you), I suggest an alternative. 从@VamsiKrishna借用代码(谢谢),我建议您选择一种方法。 In my opinion, you want to send the changes to the server, and update the local state on success. 我认为,您希望将更改发送到服务器,并在成功时更新本地状态。 Here is a working example. 这是一个工作示例。

To prevent duplicating logic, abstract the change into a function. 为防止逻辑重复,请将更改抽象为一个函数。

 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> 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM