Как загрузить данные в модуль vuex только тогда, когда это необходимо?проблемы с асинхронностью / ожиданием - PullRequest
0 голосов
/ 28 февраля 2019

Есть ли способ загрузить все данные для хранилища vuex один раз, но загружать их только при необходимости?

Я предполагаю, что есть, но я борюсь, и я не уверен, что это потому, что яЯ неправильно понимаю Vuex или Async / Await в обещаниях Javascript.

Это пример магазина для Roles.userRolesApi делает запрос axios и возвращает обещание.

import {userRolesApi} from "../api";

export default {
    actions: {
        setRoles(context, roles) {
            context.commit('SET_ROLES', roles)
        },
        async loadRoles({state, dispatch}) {
            if (state.all === null) {
                return await userRolesApi.index().then(response => {
                    dispatch('setRoles', response.data)
                })
            }
        }
    },
    state: {
        all: null
    },
    getters: {
        findRoleFromId: (state) => (role) => {
            return _.find(state.all, {id: parseInt(role)})
        },
        findRoleFromName: (state) => (role) => {
            return _.find(state.all, {name: role})
        }
    },
    mutations: {
        SET_ROLES (state, roles) {
            state.all = roles
        },
    }
}

Что я хотел бы сделать, это вызвать findRoleFromId из компонента Vue.

Это тогда получитроль из массива ролей в состоянии state.all, но также создайте этот массив из API, если он еще не существует.

Из того, что я могу сказать, это плохая практика - вместо этого делать запросы API изнутри геттерамиВместо этого у меня есть метод loadRoles в действии.

Но я не могу вызвать действие из получателя, поэтому теперь мне придется вызывать loadRoles откуда-то еще, каждый раз, когда ядумаю, мне, возможно, понадобится использовать роль.

Поэтому я получаю такой компонент:

mounted() {
    this.$store.dispatch('loadRoles')
},
computed: {
    role() {
        // This will be null at first but update once the api request finishes.
        return this.$store.getters.findRoleFromId(this.roleId)
    }
},

Это на самом деле прекрасно работает!

Однако, если по какой-то причинеЯ вызываю this.$store.dispatch('loadRoles') в двух компонентах в быстрой последовательности, тогда он дважды сделает запрос API.

Я попытался решить эту проблему с помощью async / await, но, похоже, это не имеет значения, это не такостановите обработку до завершения запроса.

В качестве теста изменив мой компонент на это:

mounted() {
    this.$store.dispatch('loadRoles')
    this.$store.dispatch('loadRoles')
    this.$store.dispatch('loadRoles')
    this.$store.dispatch('loadRoles')
},
computed: {
    role() {
        return this.$store.getters.findRoleFromId(this.roleId)
    }
},

Вызывает запрос API, который будет вызван 4 раза мгновенно.Вместо того, чтобы ждать, пока закончится первый, а затем со второй попытки, не пройдя проверку state.all === null и не сделав запрос API.

Я пытался быть настолько многословным, насколько это возможно, объясняя, что это такое.Я пытаюсь сделать, потому что я не уверен, где это, я иду не так.Отсюда мои вопросы:

  1. Каков наилучший способ заполнить магазин vuex только тогда, когда это необходимо?
  2. Если мой подход - хороший способ сделать это, что не так смоя попытка асинхронного / ожидания?

1 Ответ

0 голосов
/ 28 февраля 2019

Ваши компоненты не должны заботиться о том, сколько раз они запрашивают ресурс, как и пользователь RolesApi, я думаю.Если в конечном итоге вы используете fetch, вы можете кэшировать обещание, если оно не разрешено или отклонено, а последующие запросы просто возвращают это обещание.

const fetchOnce = ((cache) => (url, config) => {
    const cacheKey = `${JSON.stringify(config)} ${url}`;
    if (cache[cacheKey]) {
        cache[cacheKey] = axios.get(url, config)
            .then((result) => {
                delete cache[cacheKey];
                return result;
            })
            .catch((error) => {
                delete cache[cacheKey];
                return Promise.reject(error);
            });
    }
    return cache[cacheKey];
})({});
...