DEV Community

Cover image for Write a simple global state management for Vue 3
Robert
Robert

Posted on • Edited on

Write a simple global state management for Vue 3

If you want to manage a simple global state and didn't want to use Vuex, we can use the reactive and toRefs properties from the Vue 3 Composition API to compose our own lightweight state management.

We can start off by writing our useTodos hook that will contain our state logic.

// store/todos.js
import { reactive, toRefs } from "vue";

const url = 'https://jsonplaceholder.typicode.com/todos';

const state = reactive({
    todos: [],
    loading: true,
    selectedTodo: null
});

export default function useTodos() {
    const fetchTodos = async () => {
        state.loading = true;
        state.todos = await (await fetch(url)).json();
        state.loading = false;
    }

    return {
        ...toRefs(state), // convert to refs when returning
        fetchTodos
    }
}
Enter fullscreen mode Exit fullscreen mode

The area of focus in the code above is the reactive and toRefs properties.

The reactive property, from the docs, takes an object and returns a reactive proxy of the original. This is equivalent to 2.x's Vue.observable().

The toRefs property converts the reactive object to a plain object and lets the consuming component destructure / spread the returned object from a composition function (useTodos) without losing reactivity.

Next, we can go to our consuming component, App.vue for example, and import our composition function.

App.vue

<template>
  <h1 v-if="loading">Loading...</h1>
  <div v-else>
    <SelectedTodo />
    <ul>
      <li v-for="t in todos" :key="t.id" @click="selectedTodo = t">{{t.title}}</li>
    </ul>
  </div>
</template>

<script>
import { onMounted } from "vue";
import useTodos from "./store/todos";
import SelectedTodo from "./components/SelectedTodo.vue";

export default {
  name: "App",
  components: { SelectedTodo },
  setup() {
    const { todos, fetchTodos, selectedTodo, loading } = useTodos();

    onMounted(() => {
      fetchTodos();
    });

    return {
      todos,
      selectedTodo,
      loading,
    };
  },
};
</script>
Enter fullscreen mode Exit fullscreen mode

SelectedTodo.vue

<template>
  <pre>
    {{ JSON.stringify(selectedTodo, undefined, 4) }}
  </pre>
</template>

<script>
import useTodos from "../store/todos";

export default {
  setup() {
    const { selectedTodo } = useTodos();

    return {
      selectedTodo,
    };
  },
};
</script>
Enter fullscreen mode Exit fullscreen mode

Let's break down what we've written in App.vue:

First, we imported our composition function, destructured and returned so that it will be available for the rest of the component.

The onMounted lifecycle method was used to fetch our todos and we display the result in an unordered list. Each item is clickable and will set the value of the selectedTodo state to the selected item.

Lastly, we created a SelectedTodo.vue component just to show that the selectedTodo property is reactive.

Probably the most interesting part here is that we can reuse our hook in different components and they will get all the updates of our state.

Thanks for reading!

Cover photo by @adigold1 on Unsplash

Top comments (2)

Collapse
 
sanchezdav profile image
David Sanchez

Thanks for sharing, looks pretty easy 🙌

Collapse
 
daydaychao profile image
daydaychao

It's very useful thanks.