1
votes

I have built an app in Vue. It consists of a number of separate modules, each of which corresponds to a route and has a top-level component (and many sub-components/children). Each module has its own store, actions, mutations and getters, as well as API calls dispatched in the created() hooks of components to fetch necessary data.

This is the structure of my app:

Module structure

Candidates.vue

created() {
    this.$store.dispatch('$_candidates/getAllCandidates');
  },

/modules/candidates/_store/actions.js

import api from '../_api';

const getAllCandidates = (context) => {
  api.fetchAllCandidates
    .then((response) => {
      context.commit('ALL_CANDIDATES_FETCHED', response.data.candidate_list);
    })
    .catch((error) => {
      // eslint-disable-next-line
      console.error(error);
    });
};

/modules/candidates/_api/index.js

import { fetchData } from '@/helpers';

const allCandidatesEndpoint =
  'https://myapiendpoint.io/candidates/list/all';
const fetchAllCandidates = fetchData(allCandidatesEndpoint, 'get');

export default {
  fetchAllCandidates,
};

In the beforeCreate() hook of App.vue, I have a helper function to register all of the application modules in one go. I do this my importing the module stores into the helper file and then registering them. This is my helper file:

helpers.js

import axios from 'axios';
import { store } from '@/store/store';

import candidatesStore from './modules/candidates/_store';
import dashboardStore from './modules/dashboard/_store';
import eventsStore from './modules/events/_store';
import loginStore from './modules/login/_store';

function fetchData(endpoint, requestType, requestBody) {
  const apiToken = store.state.apiToken;
  delete axios.defaults.auth;
  return axios.request({
    method: requestType,
    data: requestBody,
    url: endpoint,
    headers: {
      'server-token-id': apiToken,
    },
  })
    .then(response => response)
    .catch(error => error);
}

/* Register all of the Vuex modules we'll need to manage application state */

function registerVuexModules() {
  store.registerModule('$_candidates', candidatesStore);
  store.registerModule('$_dashboard', dashboardStore);
  store.registerModule('$_events', eventsStore);
  store.registerModule('$_login', loginStore);
}

function unregisterVuexModules() {
  store.unregisterModule('$_candidates', candidatesStore);
  store.unregisterModule('$_dashboard', dashboardStore);
  store.unregisterModule('$_events', eventsStore);
  store.unregisterModule('$_login', loginStore);
}

export {
  fetchData,
  registerVuexModules,
  unregisterVuexModules,
};

...and I import into App.vue like this:

beforeCreate() {
  registerVuexModules();
},

However, the import of each module somehow triggers an API call (using the fetchData function), which returns a 401. I have confirmed this by commenting out various parts of helpers.js - and it is definitely the import rather than the functions themselves.

When I remove the import of a module store into helpers.js, the API call is not attempted for that module's top-level component. The weird parts for me are:

  1. Even though the actions that should trigger these API call are only dispatched in the top level components of each module, the API calls are attempted every time I reload the login page, even before these components are created;

  2. Vue-dev-tools does not register the corresponding events for the actions being dispatched;

  3. If I remove all of the store imports from the helper file, no API calls happen.

I have tried changing the format of my imports in vue-router so that components are lazy-loaded, as I thought this might be the issue. The bundle size decreased but it did not fix the phantom API calls. This is how I import them...

/router/index.js

import Vue from 'vue';
import Router from 'vue-router';
import axios from 'axios';

import { store } from '../store/store';

/* Lazy load all of the components required for the routes */

const Login = () => import(/* webpackChunkName: "login" */
  '@/modules/login/Login');

const Dashboard = () => import(/* webpackChunkName: "dashboard" */
  '@/modules/dashboard/Dashboard');

...

const router = new Router({

  routes: [
    {
      path: '/',
      name: 'root',
      component: Login,
    },
    {
      path: '/login',
      name: 'login',
      component: Login,
    },
    {
      path: '/dashboard',
      name: 'dashboard',
      component: Dashboard,
      beforeEnter: (to, from, next) => {
        guard(to, from, next);
      },
    },
...

Can anyone explain this behaviour or what I've missed?

1
Can you show how you create the stores?FK82

1 Answers

3
votes

From what I can tell, you have this line

const fetchAllCandidates = fetchData(allCandidatesEndpoint, 'get');

This means that every time you import, it is running the fetchData function and returning the results.

Maybe you meant to do this instead.

const fetchAllCandidates = function ()
{
  return fetchData(allCandidatesEndpoint, 'get');
}