Vuexアクションからの約束の回復


130

私は最近、jQからより構造化されたフレームワークであるVueJSへの移行を開始しました。

概念的には、Vuexは私にとって少しパラダイムシフトでしたが、私は今、そのすべてが何であるかを知っており、それを完全に理解していると確信しています!しかし、ほとんどが実装の観点から、いくつかの小さな灰色の領域が存在します。

これは設計上は良いと感じますが、単方向データフローのVuex サイクルと矛盾するかどうかはわかりません。

基本的に、アクションからpromise(like)オブジェクトを返すことは良い習慣と考えられていますか?私はこれらを非同期状態のラッパーとして扱い、障害などの状態があるので、promiseを返すのに適しているようです。逆にミューテーターは物事を変えるだけで、ストア/モジュール内の純粋な構造です。

回答:


255

actionsVuexでは非同期です。呼び出し側の関数(アクションの開始者)にアクションが完了したことを知らせる唯一の方法は、Promiseを返して後で解決することです。

次に例を示しmyActionます。を返し、Promisehttp呼び出しを行い、後者を解決または拒否しPromiseます-すべて非同期で

actions: {
    myAction(context, data) {
        return new Promise((resolve, reject) => {
            // Do something here... lets say, a http call using vue-resource
            this.$http("/api/something").then(response => {
                // http success, call the mutator and change something in state
                resolve(response);  // Let the calling function know that http is done. You may send some data back
            }, error => {
                // http failed, let the calling function know that action did not work out
                reject(error);
            })
        })
    }
}

これで、Vueコンポーネントがを開始myActionすると、このPromiseオブジェクトを取得して、成功したかどうかを知ることができます。Vueコンポーネントのサンプルコードを以下に示します。

export default {
    mounted: function() {
        // This component just got created. Lets fetch some data here using an action
        this.$store.dispatch("myAction").then(response => {
            console.log("Got some data, now lets show something in this component")
        }, error => {
            console.error("Got nothing from server. Prompt user to check internet connection and try again")
        })
    }
}

上記のように、actionsを返すことはにとって非常に有益ですPromise。そうしないと、アクションの開始者が何が起こっているのか、そしてユーザーインターフェイスに何かを表示するのに十分安定していることを知る方法がありません。

そして最後の注意mutators-あなたが正しく指摘したように、それらは同期しています。それらはの内容を変更し、state通常はから呼び出されactionsます。ミックスする必要はありませんPromisesmutatorsて、actionsその部分ハンドルが。

編集:単方向データフローのVuexサイクルに関する私の見解:

this.$store.state["your data key"]コンポーネントのようにデータにアクセスする場合、データフローは単方向です。

アクションからの約束は、アクションが完了したことをコンポーネントに知らせることだけです。

コンポーネントは、上記の例のプロミス解決関数からデータを取得するか(単方向ではないため、推奨されません)、直接そこから$store.state["your data key"]単方向であり、vuexデータのライフサイクルに従います。

上記の段落ではVue.set(state, "your data key", http_data)、アクションでhttp呼び出しが完了すると、ミューテーターがを使用することを前提としています。


4
「上記のように、アクションがPromiseを返すことは非常に有益です。それ以外の場合は、アクションの開始者が何が起こっているのか、そしてユーザーインターフェースに何かを表示するのに十分安定していることを知る方法はありません。」IMO、これはVuexのポイントが欠けています。アクションの開始者は、何が起こっているかを知る必要はありません。アクションは、非同期イベントからデータが返されたときに状態を変更する必要があり、コンポーネントは、PromiseではなくVuexストアの状態に基づいてそのステージの変更に応答する必要があります。
ceejayoz 2016年

1
@ceejayoz同意しました。状態は、すべてのデータオブジェクトの単一の真の情報源である必要があります。しかし、約束はアクションの開始者に通信する唯一の方法です。たとえば、httpの失敗後に[再試行]ボタンを表示したい場合、その情報は状態になりませんが、を介してのみ通信できますPromise.reject()
Mani

1
これはVuexストア内で簡単に処理できます。アクション自体が、コンポーネントが処理できるfailedを設定するミューテーターを起動state.foo.failed = trueできます。そのためにコンポーネントにプロミスを渡す必要はありません。おまけとして、同じ失敗に対応したい他のすべてのものがストアからも行うことができます。
ceejayoz

4
@ceejayoz ドキュメントの構成アクション(最後のセクション)を 確認してください-vuex.vuejs.org/en/actions.html-アクションは非同期であるため、これらのドキュメントで述べられているように、Promiseを返すことは良い考えです。上記の$ httpのケースではないかもしれませんが、アクションが完了したときを知る必要がある場合もあります。
Mani

6
@DanielParkはい、シナリオと個々の開発者の好みに「依存します」。私の場合、私{isLoading:true}は私の州のような中間的な値を避けたかったので、プロミスに頼りました。好みは異なる場合があります。結局のところ、私たちの目的は、混乱のない保守可能なコードを書くことです。Promiseがその目的を達成するかどうか、またはVuexの状態かどうかは、個々の開発者とチームが決定する必要があります。
Mani

41

クローズドトピックに関する情報だけのために: promiseを作成する必要はありません。axiosはそれ自体を返します。

参照:https : //forum.vuejs.org/t/how-to-resolve-a-promise-object-in-a-vuex-action-and-redirect-to-another-route/18254/4

例:

    export const loginForm = ({ commit }, data) => {
      return axios
        .post('http://localhost:8000/api/login', data)
        .then((response) => {
          commit('logUserIn', response.data);
        })
        .catch((error) => {
          commit('unAuthorisedUser', { error:error.response.data });
        })
    }

もう一つの例:

    addEmployee({ commit, state }) {       
      return insertEmployee(state.employee)
        .then(result => {
          commit('setEmployee', result.data);
          return result.data; // resolve 
        })
        .catch(err => {           
          throw err.response.data; // reject
        })
    }

async-awaitの別の例

    async getUser({ commit }) {
        try {
            const currentUser = await axios.get('/user/current')
            commit('setUser', currentUser)
            return currentUser
        } catch (err) {
            commit('setUser', null)
            throw 'Unable to fetch current user'
        }
    },

axiosアクションはデフォルトですでに非同期なので、最後の例は冗長ではありませんか?
nonNumericalFloat

9

行動

ADD_PRODUCT : (context,product) => {
  return Axios.post(uri, product).then((response) => {
    if (response.status === 'success') {  
      context.commit('SET_PRODUCT',response.data.data)
    }
    return response.data
  });
});

成分

this.$store.dispatch('ADD_PRODUCT',data).then((res) => {
  if (res.status === 'success') {
    // write your success actions here....
  } else {
     // write your error actions here...
  }
})

2
コンポーネントで定義されていないこの機能しない応答
Nand Lal

1
ADD_PRODUCT関数でリターンを追加するのを忘れたと思います
バスカララオグミディ

「axios」では小文字の「a」にする必要があります。
bigp

axios

0

TL:DR; 必要なときにのみアクションからpromiseを返しますが、同じアクションをDRYチェーンします。

長い間、私はまた、戻るアクションが単方向データフローのVuexサイクルと矛盾しています。

しかし、あなたの行動から約束を返すことが「必要」かもしれないエッジケースがあります。

2つの異なるコンポーネントからアクションをトリガーできる状況を想像してみてください。それぞれが異なる方法で障害ケースを処理します。その場合、ストアにさまざまなフラグを設定するには、呼び出し元コンポーネントをパラメーターとして渡す必要があります。

ばかげた例

ユーザーがnavbarと/ navbarを含む/ profileページでユーザー名を編集できるページ。どちらも、非同期のアクション「ユーザー名の変更」をトリガーします。promiseが失敗した場合、ページには、ユーザーがユーザー名を変更しようとしたコンポーネントのエラーのみが表示されます。

もちろんそれはばかげた例ですが、コードを複製して2つの異なるアクションで同じ呼び出しを行わずにこの問題を解決する方法はありません。


-1

actions.js

const axios = require('axios');
const types = require('./types');

export const actions = {
  GET_CONTENT({commit}){
    axios.get(`${URL}`)
      .then(doc =>{
        const content = doc.data;
        commit(types.SET_CONTENT , content);
        setTimeout(() =>{
          commit(types.IS_LOADING , false);
        } , 1000);
      }).catch(err =>{
        console.log(err);
    });
  },
}

home.vue

<script>
  import {value , onCreated} from "vue-function-api";
  import {useState, useStore} from "@u3u/vue-hooks";

  export default {
    name: 'home',

    setup(){
      const store = useStore();
      const state = {
        ...useState(["content" , "isLoading"])
      };
      onCreated(() =>{
        store.value.dispatch("GET_CONTENT" );
      });

      return{
        ...state,
      }
    }
  };
</script>
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.