loading...
Cover image for Write a simple global state management for Vue 3

Write a simple global state management for Vue 3

wobsoriano profile image Robert ・2 min read

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
    }
}

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>

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>

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

Discussion

pic
Editor guide
Collapse
sanchezdav profile image
David Sanchez

Thanks for sharing, looks pretty easy 🙌