Write a simple global state management for Vue 3
Published: 2020-09-19 01:06:01

If you want to manage a simple global state and didn't want to use Vuex, we can use the [reactive](https://composition-api.vuejs.org/api.html#reactive) and [toRefs](https://composition-api.vuejs.org/api.html#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.

```javascript
// 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*

```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*

```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](https://unsplash.com/@adigold1) on [Unsplash](https://unsplash.com/)*