web-dev-qa-db-ja.com

コンポーネントをロードする前に、VueX値がロードされるのを待ちます

ユーザーがコンポーネントのURLを直接ナビゲートしようとすると、vuexアクションでhttp呼び出しが行われ、解決されると状態の値が定義されます。

Http呼び出しが解決され、状態値が定義されるまで、コンポーネントをロードしたくありません。

たとえば、私のコンポーネントで

export default {
  computed: {
    ...mapState({
      // ** this value needs to load before component mounted() runs **
      asyncListValues: state => state.asyncListValues
    })
  },

  mounted () {
    // ** I need asyncListValues to be defined before this runs **
    this.asyncListValues.forEach((val) => { 
      // do stuff
    });
  }
}

コンポーネントをロードする前に、コンポーネントがasyncListValuesのロードを待機するようにするにはどうすればよいですか?

3
Edon

それを行う方法は、状態値を格納することです。

たとえば、ストアが単一のAPIに依存している場合は、次のようにします。ただし、複数のAPIの場合、各APIのロード状態を個別に保存するか、各APIに専用のオブジェクトを使用することをお勧めします。

あなたが持つことができる通常4つの状態がありますが、私はグローバルにアクセス可能なモジュールに入れたいです:

// enums.js
export default {
  INIT: 0,
  LOADING: 1,
  ERROR: 2,
  LOADED: 3
};

次に、変数をvuex状態に格納し、apiStateをINITで初期化します。 []で配列を初期化することもできますが、それは必要ありません。

import ENUM from "@/enums";
// store.js
export default new Vuex.Store({
  state: {
    apiState: ENUM.INIT,
    accounts: [],
    // ...other state
  },
  mutations: {
    updateAccounts (state, accounts) {
      state.accounts = accounts;
      state.apiState = ENUM.LOADED;
    },
    setApiState (state, apiState) {
      state.apiState = apiState;
    },
  },
  actions: {
    loadAccounts ({commit) {
      commit('setApiState', ENUM.ERROR);
      somFetchInterface()
        .then(data=>commit('updateAccounts', data))
        .catch(err=>commit('setApiState', ENUM.ERROR))
    }
  }
});

次に、computedsを追加することで、表示するコンポーネントを切り替えることができます。これは、エラー状態を簡単に識別できるため、状態を使用する利点を示し、状態が準備できていないときに読み込みアニメーションを表示します。

<template>
  <ChildComponent v-if="apiStateLoaded"/>
  <Loader v-if="apiStateLoading"/>
  <Error v-if="apiStateError"/>
</template>
<script>
import ENUM from "@/enums";
export default {
  computed: {
    ...mapState({
      apiState: state=> state.apiState
    }),
    apiStateLoaded() {
      return this.apiState === ENUM.LOADED;
    },
    apiStateLoading() {
      return this.apiState === ENUM.LOADING || this.apiState === ENUM.INIT;
    },
    apiStateError() {
      return this.apiState === ENUM.ERROR;
    },
  })
}
</script>

または、ストアでasyncListValuesを空の配列[]で初期化するだけで、配列を予期するエラーを回避できます。

1
Daniel

1つのアプローチは、コンポーネントを2つの異なるコンポーネントに分割することです。新しい親コンポーネントは、データの準備ができたら、データのフェッチと子コンポーネントのレンダリングを処理できます。

ParentComponent.vue

<template>
  <child-component v-if="asyncListValues && asyncListValues.length" :asyncListValues="asyncListValues"/>
  <div v-else>Placeholder</div>
</template>
<script>
export default {
  computed: {
    ...mapState({
      asyncListValues: state => state.asyncListValues
    })
  }
}
</script>

ChildComponent.vue

export default {
  props: ["asyncListValues"],
  mounted () {
    this.asyncListValues.forEach((val) => { 
      // do stuff
    });
  }
}
1
Connor

質問でvue-routerについて言及したので、コンポーネントのレンダリングを延期するために作成されたbeforeRouteEnterを使用できます。

たとえば、「写真」というルートがある場合:

import Photo from "../page/Photo.vue";

new VueRouter({
  mode: "history",
  routes: [
    { name: "home", path: "/", component: Home },
    { name: "photo", path: "/photo", component: Photo }
  ]
});

次のようにbeforeRouteEnterを使用できます。

<template>
  <div>
    Photo rendered here
  </div>
</template>
<script>
export default {
  beforeRouteEnter: async function(to, from, next) {
    try {
      await this.$store.dispatch("longRuningHttpCall");

      next();
    } catch(exception) {
      next(exception);
    }
  }
}
</script>

それが行うことは、アクションが完了するのを待ち、必要に応じて状態を更新してから、next()を呼び出すと、ルーターがプロセスを続行するように指示されます(<router-view></router-view>内のコンポーネントをレンダリングします)。

ES6を使用しない例が必要かどうか(たとえば、この構文を使用しない場合)を教えてください。

このページで beforeRouteEnterの公式ドキュメント を確認できます。また、beforeEnterを使用してルートレベルに配置することもできます。

1
Anwar