# Stores

Nodewood uses Vuex (opens new window) to handle state management. Stores are stored in a feature's ui/stores folder, and configured in the feature's ui/init.js file:

initStores(store) {
  /* eslint-disable global-require */
  store.registerModule('Example', require('#features/examples/ui/stores/ExampleStore'));
  // DO NOT REMOVE: Generated stores will be added above this line
  /* eslint-enable */
}

# Using stores

Stores are namespaced, which means they can be used in your apps as follows:

<template>
  <div>
    <button @click="sendExample">Click here</button>
  </div>
</template>

<script setup>
import { useStore } from 'vuex';

const store = useStore();

const sendExample = (values) => store.dispatch('Example/sendExample', values);
</script>

# Waiting on network access

Should you wish to display a spinner or something similar to indicate to the user that network access is happening, Nodewood includes the helper library vue-is-loading (opens new window):

<template>
  <div>
    <button
      :disabled="$isLoading('sendExampleLoading')"
      @click="sendExample"
    >
      Click here
    </button>
  </div>
</template>

<script setup>
import { ref, getCurrentInstance } from 'vue';
import { useStore } from 'vuex';
import { loadable } from 'vue-is-loading';

const store = useStore();

const sendExample = loadable(
  (values) => store.dispatch('Example/sendExample', values),
  'sendExampleLoading',
  getCurrentInstance(),
);
</script>

To prevent a "flicker" effect, where forms are disabled for only a fraction of a second and users are unsure if their actions have taken effect, a helper method called delayMin has been provided, to ensure a minimum amount of time has been waited on before the async function returns. Caution must be taken to ensure this value is not set too high, however.

Typically, you would use this in your store, not your components:

async confirmEmail({ commit }, { token }) {
  await delayMin(
    500,
    request.post('/api/public/confirm-email').send({ token }),
  );
},

# Adding a new store

To add a new store with some sample code to get you rolling, run:

nodewood add:store FEATURE NAME

Replace FEATURE with the name of the feature you wish to add a store for, and NAME with the name of the store you wish to create. (e.g.: nodewood add:store scheduling calendar to add CalendarStore to the scheduling feature.) By default, stores will be registered in the feature's ui/init.js, but if you wish to register the store manually, add --no-init to the end of the command.

# Adding watchers

Sometimes, you may want to update or calculate store values based on data from another module (e.g. based on a value of the current user). The easiest way to do this is to add a watcher, but where? Sometimes there may be no good single Component to add this watcher to, as the update/calculation is more global in nature.

In this case, we can add this watcher to the optional initWatchers function, configured in the feature's ui/init.js file. These functions are only called after every store has been loaded, ensuring it is safe to refer to any other store in the watchers you configure.

An example is where we load/update the StripeConfig in the SubscriptionsStore based on the currently-loaded user's currency:

/**
 * Initialize watchers on Vuex stores for this feature.
 *
 * @param {Vuex} store - Vuex store to intialize store modules.
 */
initWatchers(store) {
  store.watch(
    (state) => state.ActiveUser.user,
    (watched) => {
      store.commit('Subscriptions/initializeStripeConfig', { currency: watched.currency });
    },
  );
}