loading...
Cover image for To-Do app: Composition API as an alternative to Vuex

To-Do app: Composition API as an alternative to Vuex

diegolepore profile image Diego Palacios Lepore ・3 min read

After reading an Anthony Gore's article about using the new Composition API as some sort of replacement of Vuex, for smaller projects, I took a simple todo app I built in Codepen, and then I created a new Vue 3 app (using the vue cli) and lastly, I moved all the state and mutation methods from each component to one single file (global.js - which will be something like the store, in Vuex).

Source code and foreword

Here's a list of the source code and the Codepen I will refer to in this article:

Codepen: Vue To-do app

GitHub repo: todo-app-vue3

Netlify: https://relaxed-yonath-fa8475.netlify.app/

If you take a look at the todo app I created in Codepen you'll notice I'm not even using Vuex, I'm just using both props to pass data down to children and $emit to pass data/communicate up to parent components.

One of the advantages of the new Composition API is that now we have access to reactive features outside of components, which is quite powerful.

So here's what I did after creating my Vue 3 app, and putting the components code into its own files, and basically making the app work like it is working on Codepen:

Move the state and mutation functions to a global file

The first thing I did was to create the global.js file in /src.

Alt Text

Inside global.js, I imported the reactive and the readonly APIs. Let's take a look at the code in 'global.js' - I will add the comments in the code snippet.

import { reactive, readonly } from "vue";

/* 
Wrapping our object with reactive() makes, 
as it clearly suggests, our object reactive 
(it even affects all nested properties).
*/

const state = reactive({
  tasks: [
     {
      id: 1,
      description: "Finish the course",
      done: false,
     }, 
     {..}, 
     {..}, 
     {..}, 
     {..}
  ],
  nextId: 6,
  tasksFiltered: [],
  activeTab: "all",
})

/* 
All these functions below are a combination of
mutations and actions (when comparing with Vuex).
But, of course, we are always free to organize our code
however we want.
*/

const filterTodos = function(filterOption) {..}

const addTodo = function(todo) {..}

const deleteTask = function(task) {..}

const toggleTaskStatus = function(task) {..}

// Export an object with the state and mutations
export default { 
  // With readonly(), we prevent our state to be mutated
  // outside of the global.js module
  state: readonly(state), 
  filterTodos, 
  addTodo, 
  deleteTask, 
  toggleTaskStatus
}

Use Provide / inject

Then, we need to make global.js (our "custom store") accesible to all of the App.vue child components. To do so, we have to use the provide property inside our App.vue in order to make global.js available to all the child components, so we import global.js in App and then, we provide it.

Alt Text

Right after that, in each component, we need to inject global in order to use it on each of them.

Alt Text

Now a screenshot of each child component (but remember, you can always go to the repo and take a look a the code)

TodoList.vue

Alt Text

Form.vue

Alt Text

Header.vue

Alt Text

Alt Text

This approach can be improved, and could serve as a simpler alternative. Of course, Vuex is more debuggable and we can track mutations in the vue dev tools. So it will always depend on the project we're working on or our personal choice and point of view.

What do you think about this approach?
Do you have any suggestions?

Hope you found this article useful! πŸ™‚

Discussion

pic
Editor guide
Collapse
appurist profile image
Paul / Appurist

I've been arguing that Vuex provides little beyond the Composition API, depending on how much you use the Vuex differentiators: time travel, data access structure, and debugging.

To me, the main loss from Vuex is the structured/regulated access to data, and you have resolved that loss through your own structure above, providing a read-only state and specific methods for funneling all modifications. This makes debugging and tracing easy, when you need it. And it seems that I don't use the time travel at all, I just haven't needed it or even found it useful. It makes for an impressive demo, but benefit to me does not exceed the cost of the overhead boilerplate of Vuex, which I find makes it more difficult to debug (tracing or breakpointing Vuex dispatch vs debugging a modification function like those provided in your article).

Thank you for posting this, it is triggering a lot of thought in my mind for how I'd structure my Composition API state access; probably exactly the way you did it.

Collapse
diegolepore profile image
Diego Palacios Lepore Author

Thanks for your thorough comment about this Paul πŸ˜ƒ.

And I'm glad it makes you think about how to structure Composition API in your projects.

Experimenting with these new features, reading your comments about it and trying to think on different approaches is quite exiting.

Cheers! ✌️

Collapse
fadamakis profile image
Fotis Adamakis

You are losing the debugging and time travel capabilities of vuex without really any benefit. Cool proof of concept anyway!

Collapse
diegolepore profile image
Diego Palacios Lepore Author

Absolutely, agree. That's clearly one of the caveats. One of the benefits I see when using this pattern in smaller projects is the fact that we don't have Vuex in our dependencies, and our projects can be even more lightweight.

Collapse
rckm profile image
Eric Kim

It is remind me of Context API in React

Collapse
azabroflovski profile image
Aza

Cool

Collapse
diegolepore profile image
Collapse
sanchezdav profile image
David Sanchez

Awesome! Nice approach! πŸ”₯

Collapse
diegolepore profile image
Diego Palacios Lepore Author

Thanks David!! 🀘