Comparing Vuex and Redux by developing an application

Preetish HS
Feb 17 · 7 min read

I have been using Vue and React for quite some time now.

Being JavaScript developers we get the opportinity to work on multiple frameworks and various state management systems. Since I am a freelance web developer I use both React and Vue based on the project requirements. In this story, I’ll compare the working of two very popular state management systems, Vuex and Redux. Though both are inspired by Flux Architecture, they follow a different way to achieve the result.

It's a natural tendency to compare the features of one framework to a previously learned framework or library while learning the new one. That gives an easy way to understand and remember because we know how it works in the other.

So here is an article about comparing how Vuex and Redux do the same things in different ways.

Note: I am not comparing the core frameworks, nor am I trying to tell one is better compared to the other, I am concentrating only on features of Vuex and Redux and how both of them achieve the result in a different way.

The HTML

The markup of both applications are exactly the same for now, later we’ll use directives in Vue and Render functions in React to modify them.

Getting Started

Let’s first show you how to start using these state management libraries and their basic boilerplate code.

Vuex

Vuex is tightly coupled with VueJS hence there is less boilerplate code for Vuex to get started with.

Redux

Redux is framework agnostic, It is not specific to React. Hence we need to import few other libraries for it to work.

Store and State

Both Vuex and Redux have a single store object which maintains all the application state variables, Let’s see how to create state variables in store.

How Vuex does it

Vuex state is mutable, hence we can directly create state variables and assign values to them.

How Redux does it

Redux uses Reducers, which are pure functions that take the previous state and action, and return the next state. we’ll talk more about this below.

Redux uses Reducers to create and manage a set of states.

Using these states in our application

Now that we were able to create a state with one TODO item hardcoded, Let’s see how we can use this in our application.

How Vuex does it

Vue has a mapState() helper function to map the states from our Vuex store to our components. These mapped variables can then be accessed directly like normal state variables, though we cannot modify these variables directly.

If we need to perform some operation on our state variables and get the computed value to use in various components, we can make use of Vuex’s getters by using this in our template:

<li v-for="(item, index) in todoList" 
:key="item.id"
:class="{ completed: item.completed}"
>

How Redux does it

Redux has mapStateToProps() method which is passed to a higher order component connect provided by react-redux library. These states are now accessible as props in our component.

//component
import { connect } from 'react-redux';
const mapStateToProps = (state) => {
return { todos: state };
}
export default connect(mapStateToProps)(App);

Redux doesn’t provide any similar feature to getters, we can write our own utility methods in a separate file and import them wherever necessary by using this in the template:

renderList() {
return this.props.todos.map(item => {
return (
<li key={item.id}
className={"todo " + (item.completed ? "completed" : "")}
onClick={() => this.props.toggleCompletion(item.id)}>
</li>
)
})
}

Modifying the State

A state variable should not be modified directly. We use special methods to modify/update them so that it can be tracked properly.

How Vuex does it

The only way to actually change state in a Vuex store is by committing a mutation. Vuex mutations are very similar to events; each mutation has a string type and a handler. The handler function is where we perform actual state modifications, and it will receive the state as the first argument.


//store.js
mutations
: {
addItem(state, payload) {
state.todos.push({id:GLOBAL_ID++, title: payload, completed: false});
},
togglecompletion(state, id) {
state.todos.forEach( item => {
if(item.id === id)
item.completed = !item.completed;
})
},
removeItem(state, index) {
state.todos.splice(index, 1);
}
}

Mutations also take payload as an optional second argument, If we have to pass more data then we can either send array or objects in payload.

How Redux does it

In Redux the state modification methods are also written in reducers.

//reducer/index.jsconst todos = (state = initialState, action) => {
switch (action.type) {
case "ADD_ITEM":
return [
...state,
{
id: GLOBAL_ID++,
title: action.title,
completed: false
}
];
case "TOGGLE_COMPLETION":
console.log('action', action);
return state.map(todo =>
todo.id === action.id ? { ...todo, completed: !todo.completed } : todo
);
case "REMOVE_ITEM":
return state.filter(todo => todo.id !== action.id);
default:
return state;
}
};

Reducers maintain both state and their modification methods, These methods are called by dispatching actions. These actions also take payload to send data from our application to our Redux store. (Remember, in Redux states are immutable)

//actions/index.jslet nextTodoId = 0;export const addItem = title => {
return {
type: "ADD_ITEM",
id: nextTodoId++,
title
};
};
export const toggleCompletion = id => {
return {
type: "TOGGLE_COMPLETION",
id
};
};
export const removeItem = id => {
return {
type: "REMOVE_ITEM",
id
}
};

We also create a new file to store all our actions, This is not a compulsion but it makes our code look more organized. Rather than doing it in the later part of the code, let’s do that now.

Modifying the State from our Components

How Vuex does it

Vuex provides a helper method mapMutations() to access our mutations in the components.

methods: {
...mapMutations([
'addItem',
'togglecompletion',
'removeItem',
])
}

After mapping, these methods can then be accessed like normal component methods by using these mutations in our component:

<button class="destroy" @click.stop="removeTodo(index)"></button>removeTodo: function(index) {
this.removeItem(index);
}

How Redux does it

Similar to mapStateToProps() Redux provides us with another helper called mapDispatchToProps() passed to our HOC.

const mapDispatcherstoProps = dispatch =>  {
return {
toggleCompletion: (id) => dispatch(toggleCompletion(id)),
removeItem: (id) => dispatch(removeItem(id)),
addItem: (title)=> dispatch(addItem(title)),
addItemFromWeb: ()=> dispatch(addItemFromWeb())
}
}
export default connect(mapStateToProps, mapDispatcherstoProps)(App);

This method gets dispatch as an argument, this dispatch method is used to commit our actions. These actions are now mapped to local methods which are available through props, as such:

<button className="destroy" 
onClick={() => this.props.removeItem(item.id)}
/>

Now our TO DO LIST App is completely functional, we can add items, check completed items and remove items.

Making Async Calls

Expanding our TO DO LIST application, let’s say we want to load the list for a user stored in the server, we cannot make a call to the server directly from our Vuex mutations or Redux actions as they are synchronous methods. We need special ways to achieve this.

How Vuex does it

Mutations are pure synchronous functions, we cannot cause side effects in our mutations. To make Async calls, Vuex has actions. Actions are similar to mutations but instead of mutating the state, actions commit mutations.

//store.js
actions: {
addItemFromWeb(context) {
axios.get(')
.then((response) => {
console.log(response);
context.commit('addItem', response.data.title)
})
.catch((error) => console.log(error));
}
}

In the above example, we make use of axios library to make HTTP call.

To use these actions in our components, Vuex provides us with mapActions() helper method.

<button @click="addItemFromWeb"> Async Add </button>methods: {
...mapActions([
'addItemFromWeb'
])
}

How Redux does it

Redux does not provide any out of the box solution as Vuex does, hence we need Middlewares to make Async calls.

To achieve this we use a middleware called redux-thunk. This middleware is very simple it checks whether the action is a function. If it is, then that function is invoked with dispatch. If not, the reducers are called directly.

import { applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
const store = createStore(
reducer,
applyMiddleware(thunk),
);

Now in actions.js we create our async function:

export const addItemFromWeb = () => {
return dispatch => {
axios.get(')
.then((response) =>{
console.log(response);
dispatch(addItem(response.data.title));
})
.catch((error) => {
console.log(error);
})
}
}

Application Management and Scaling

As our application grows we would have more states to manage, we cannot have a single store.js file for Vuex or a single reducer.js file for Redux, we need to modularise our application.

How Vuex does it

Vuex allows us to divide our store into modules. Each module can contain its own state, mutations, actions, getters, and even nested modules.

const moduleA = {
state: { ... },
mutations: { ... },
actions: { ... },
getters: { ... }
}

const moduleB = {
state: { ... },
mutations: { ... },
actions: { ... }
}

const store = new Vuex.Store({
modules: {
a: moduleA,
b: moduleB
}
})

store.state.a // -> `moduleA`'s state
store.state.b // -> `moduleB`'s state

Inside a module’s mutations and getters, the first argument received will be the module’s local state.

Each module can be written in separate files which can be imported in store.js

How Redux does it

We can split our root reducer into multiple reducers and then combine them together. Each reducer will be responsible for managing the states within them.

import { combineReducers } from 'redux' 
import todos from './todos'
import counter from './counter'

let reducers = combineReducers({
todo: todos,
ctr: counter
})
const store = createStore(reducer);ReactDOM.render(<Provider store={store}><App /></Provider>, document.getElementById('root'));

Redux library provides us with a feature called combineReducers to combine all our reducers into a single reducer. (Note that we cannot directly access the states present in one reducer in another) data required for a reducer should be passed by the component through actions.

Conclusion

We compared two very popular state management libraries, both inspired by Flux architecture, both have the same end goal but takes a different path to achieve it.

I hope that you have found this useful. If so, be sure to leave lots of claps! 👏

If you’re interested in comparing Vue and React, I recommend you check out by Sunil Sandhu.

Live Demo

Demo App source code

I am also currently available for freelance web developer work. Have any projects for me? Be sure to drop me an email at contact@preetish.in 😃

JavaScript in Plain English

Learn the web's most important programming language.

Preetish HS

Written by

Freelance Web Developer, digital nomad and a design enthusiast. www.preetish.in

JavaScript in Plain English

Learn the web's most important programming language.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade