HomeBlogVue 3 External Store

Using an external store in Vue 3

Published Jan 26, 2023
Updated Dec 15, 2024
1 minutes read

In this article, we will see how to use an external store in Vue 3 using the composition API. Also, will use the publish-subscribe pattern to achieve this. I'm not a Vue wizard, so if you see something wrong, please let me know.

As I said in the Implement the publish-subscribe pattern in TypeScript article, I'm working on a refector of a personal project. To manage the state, I've decided to use the publish-subscribe pattern.

The store

 
export type ISubscribe<T> = {
  getState: () => T;
  setState: (value: Partial<T>) => void;
  subscribe: (callback: () => void) => () => void;
};
 
export const subscriber = <T>(initialValue: T): ISubscribe<T> => {
  const listeners = new Set<() => void>();
  let value = initialValue;
 
  const getState = () => value;
 
  const setState = (newValue: Partial<T>) => {
    value = { ...value, ...newValue };
    listeners.forEach((listener) => listener());
  };
 
  const subscribe = (callback: () => void) => {
    listeners.add(callback);
    // Return a function to unsubscribe.
    return () => listeners.delete(callback);
  };
 
  return {
    getState,
    setState,
    subscribe,
  };
};

The Vue 3 hook

 
import { shallowRef, watch, onUnmounted } from "vue";
import { subscriber } from "./subscriber";
 
const initialState = {
  steps: [],
  currentStep: 0,
};
 
export const useSubscriber = () => {
  const subs = subscriber(initialState);
 
  // Create a shallowRef to store the state.
  const state = shallowRef(subs.getState());
 
  // Function to update our state.
  const updater = (state) => {
    subs.setState(state);
  };
 
  // Subscribe to the store and update the state.
  const unsubscribe = subs.subscribe(() => {
    state.value = subs.getState();
  });
 
  // I don't know if this is necessary.
  onUnmounted(() => {
    unsubscribe();
  });
 
  return [state, updater];
};

The component

 
<script setup>
import { useSubscriber } from "./useSubscriber";
 
const [steppers, updater] = useSubscriber();
 
function nextStep() {
  const currentStep = steppers.value.currentStep;
  updater({ currentStep: currentStep + 1 });
}
</script>
 
<template>
  <p>current step: {{ steppers.currentStep }}</p>
  <button @click="nextStep">next step</button>
</template>

Here an example on Vue playground

References