Vuex 在异步操作完成之前访问状态

Posted

技术标签:

【中文标题】Vuex 在异步操作完成之前访问状态【英文标题】:Vuex accessing state BEFORE async action is complete 【发布时间】:2017-11-22 12:39:06 【问题描述】:

我遇到了计算 getter 在更新之前访问状态的问题,从而呈现旧状态。我已经尝试了一些事情,例如将突变与动作合并以及将状态更改为许多不同的值,但在调度完成之前仍会调用 getter。

问题

在异步操作(api 调用)完成之前访问状态。

代码结构

    组件 A 加载 API 数据。 用户点击了 1 条数据。 组件 A 将点击的数据(对象)分派给组件 B。 组件 B 加载接收到的对象。

注意

DOM 渲染得很好。这是一个控制台错误。 Vue 一直在关注 DOM 的变化并立即重新渲染。然而,控制台会接收所有内容。

目标

防止组件 B(仅称为 AFTER 组件)在组件 A 的分派完成之前运行其计算的 getter 方法。

Store.js

import Vue from 'vue'
import Vuex from 'vuex'
import axios from 'axios';

Vue.use(Vuex);

export const store = new Vuex.Store(

  state: 
    searchResult: ,
    selected: null,
  ,

  getters: 
    searchResult: state => 
      return state.searchResult;
    ,
    selected: state => 
      return state.selected;
    ,
  ,

  mutations:
    search: (state, payload) => 
      state.searchResult = payload;
    ,
    selected: (state, payload) => 
      state.selected = payload;
    ,
  ,

  actions: 
    search: (commit) => 
      axios.get('http://api.tvmaze.com/search/shows?q=batman')
        .then(response => 
          commit('search', response.data);
        , error => 
          console.log(error);
        );
    ,

    selected: (commit, payload) => 
      commit('selected', payload);
    ,
  ,

);

SearchResult.vue

<template>
<div>
  //looped
  <router-link to="ShowDetails" @click.native="selected(Object)"> 
      <p>Object</p>
  </router-link>
</div>
</template>

<script>
export default 
  methods: 
    selected(show)
      this.$store.dispatch('selected', show);
    ,
  ,

</script>

ShowDetails.vue

<template>
<div>
  <p>Object.name</p>
  <p>Object.genres</p>
</div>
</template>

<script>
export default 
  computed:
    show()
      return this.$store.getters.selected;
    ,
  ,

</script>

This image shows that the computed method "show" in file 'ShowDetails' runs before the state is updated (which happens BEFORE the "show" computed method. Then, once it is updated, you can see the 2nd console "TEST" which is now actually populated with an object, a few ms after the first console "TEST".

问题

Vuex 是关于状态监视和管理的,那么我该如何防止这个控制台错误呢?

提前致谢。

【问题讨论】:

【参考方案1】:

初始化你的状态。 与所有其他 Vue 数据一样,最好在起点初始化它,即使是空的 ''[] 但 VueJS(不确定 Angular 或 React 的行为是否相同,但我想类似)会表现得很多更好地初始化所有变量。

您可以在商店实例中定义状态的初始空值。

您会发现这不仅在这里很有帮助,例如使用表单验证,因为大多数插件对初始化数据都可以正常工作,但对非初始化数据不能正常工作。

希望对你有帮助。

【讨论】:

【参考方案2】:

如果没有搜索结果,可以尝试使用v-if来避免渲染模板

v-if="$store.getters.searchResult"

【讨论】:

【参考方案3】:

store.dispatch 可以处理触发的动作处理程序返回的 Promise,它也返回 Promise。见Composing Actions。

您可以设置您的 selected 操作以返回如下承诺:

selected: (commit, payload) => 
    return new Promise((resolve, reject) => 
        commit('selected', payload);
    );
 

然后在您的 SearchResults.vue 中,而不是使用 router-link 使用按钮并在 selected 操作的成功回调中执行 programmatic navigation,如下所示:

<template>
<div>
  //looped
  <button @click.native="selected(Object)"> 
      <p>Object</p>
  </button>
</div>
</template>

<script>
export default 
  methods: 
    selected(show)
      this.$store.dispatch('selected', show)
          .then(() => 
            this.$router.push('ShowDetails');
        );
    ,
  ,

</script> 

【讨论】:

以上是关于Vuex 在异步操作完成之前访问状态的主要内容,如果未能解决你的问题,请参考以下文章

在回流中排队异步操作

Vuex4.x(四)action的各种使用方式

Vuex入门—— 为什么要用Action管理异步操作

vuex的原理方法

Vuex 第5节 actions异步修改状态

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