I recently created a basic todo app (learning) using Vue.js, and Apollo GraphQL. When my senior (having working experience in React) started reviewing the client-side code a lot of his questions were related to Vuex, a state management pattern & library for Vue. As he had experience with Redux, terminology of Vuex was a little confusing for him. That’s where the idea of writing an article which helps developers, who have experience with one migrate to other library came to existence (yes existence!! I’d like to think that nobody in the whole world has thought of such a thing before 😏😎, even though you will find few articles if you search for it).

So without further ado, let’s dig into the differences (and some similarities) between Redux and Vuex.

The first difference you should keep in mind is that Redux was not built for any particular framework, even though a lot of articles will describe it in context of React it can easily be used with other view libraries. Vuex on other hand was built specifically for Vue.js and thus has been fine tuned for same.

Now we’ll start looking at some other differences between the two:

Implementation Differences

As both Redux and Vuex are inspired from Flux and The Elm Architecture, they have pretty much the same concept except for few implementation details:

  • In Redux, you are never expected to mutate the state (always return new state object). Whereas in Vuex, the norm is to mutate the state (thus taking full advantage of granular reactive system of Vue for efficient update).
  • In Redux, changes (creating new Object) are done to the state in a function called Reducers, for Vuex it’s an object called Mutations (explained in next section).

Reducers vs Mutations

Reducer (type of function you would pass to Array.prototype.reduce(reducer, ?initialValue)) is a function which contains the code that changes the state (can also be multiple functions).

Let’s have a look at a simple reducer first:

The reducer function takes two arguments:

  • Current state of the application ( store.state), which will be initial state when it is used for the first time.
  • An object called action (explained later), which tells the reducer where to perform the change and what the new data is.

There are few things you should never do inside a reducer:

  • Mutate its arguments,
  • Perform side effects like API calls and routing transitions.
  • Call non-pure functions, e.g. Date.now() or Math.random().

Always remember that the reducer must be pure. Given the same arguments, it should calculate the next state and return it. No surprises. No side effects. No API calls. No mutations. Just a calculation.

Mutation is an object which contains mapping to all the functions that changes the state. These functions do not have to be pure, but updating the state inside an asynchronous code is a big no. Even though, documentation of mutation does not advice it, as a good practice avoid doing anything other than state changes inside them.

Arguments to the function are:

  • Current state,
  • payload (can be of any type).

Observe that here, unlike reducers we are mutating the state. As Vuex store’s state is made reactive by Vue, Vue components observing the state will update automatically when state changes. This also means Vuex mutations are subject to the same reactivity caveats as plain Vue:

  • Prefer initializing your store’s initial state with all desired fields upfront.
  • When adding new properties to an Object, you should either:
    • Use Vue.set(obj, 'newProp', 123), or
    • Replace that Object with a fresh one ( state.obj = { ...state.obj, newProp: 123 }).

Core functionality of both reducers and mutations is the same i.e. updating the state. Difference lies in their implemented and invocation (explained later).

Actions in Redux

Actions are objects containing information and payload. They are the only source of information for the store.

Property type tells reducers what type of change is expected and is a must have field.

Invoking the changes

In Redux, invoking changes to state is called dispatching an action, whereas in Vuex it is called committing a mutation (dispatching an action means something else in Vuex).

Dispatching an action:

Committing a mutation:

If you are using object/redux style format then complete data gets passed as payload to mutation and type property tells the store which mutation handler to call. No changes are required in mutation handler function.

Async Actions

So far we’ve made changes to state object though synchronous actions, but in real world application we will need to update our state object based on some asynchronous action (API call). As reducer is a pure function we can’t have any API call in it and even mutations do not recommend changing state inside an async code. But, you can still make changes to state from or using async code in both of them.

In Redux, you can create an async action creators and handle it by injecting an external library/middleware called Redux Thunk middleware (as redux cannot handle async actions on it’s own). By using redux-thunk an action creator can return a function instead of an action object. This way, the action creator becomes a thunk. Let us see it in action:

We will discuss middlewares in more details in another part.

In Vuex, async operations can be performed by dispatching an action (yes, same naming as sync operator for Redux 😖😱). The syntax for action in Vuex is similar to that of mutations.

context object is the first parameter to action handlers. context is pretty much same as state object passed to the mutations. Difference in them arises when using modules. So, for now assume that they are same.

Actions in Vuex can be dispatched in same way as commits, which means we can also use exact same syntax as actions in Redux.

Man!! This is becoming much longer than i expected and yet, there is still so much more to cover. I guess i’ll have to split it into parts 😩. So, let’s finish this here and we’ll continue with rest of topics in next part (and then maybe one more).