Vuex vs. Redux - similarities and differences

What is state management and why it is important?

So first things first. To answer the above question, we need to know the basics. State management is a process of changing and reading the application’s inner state. The state could be small and managed locally. For example component state. Probably many of your application components have a state, to deal with user inputs. We need this to improve the user experience. If there is a page with paginated data - we need to know which page should be next and previous. Without a local state, this wouldn’t be so easy. 

📖 Dealing with really large data and looking for a solution? Read about state management with React Hooks!

But it’s only one side of the coin. This kind of “state” is achievable without external libraries and is pretty straightforward. We could use an HTML <form> element and FormData object to store and retrieve information about direct user inputs. For dealing even with mentioned pagination we can use query params. We don’t need anything, nor Redux nor even state hook. 

But what if you need information from component A in remote component G? One way is to use local (component) state and somehow propagate this data to component G, e.g by using props. Or, we can use a global state, or simpler; store. Global state is a central “single source of truth”. It can be accessed by nearly any component in your application (with some restrictions, depending on the library that you’re using).  So, you could think that if we have a global state, we don’t need local states. But it’s not that simple.  

When to use and when not to use global state?

As Dan Abramov said - “Flux libraries are like glasses: you’ll know when you need them”. But let's get a little closer to the subject - the main rule we should follow is: the global state should be used wherever there is a need for access to information by many components. In general, communication between components that don’t have parent <-> children relation is rather tricky and discouraged. But if you need your data from component A to be passed to distant G component - it is a classical ‘use-case’ of the store. 

react native apps

But you have to have in mind that using another external library isn’t always a profitable solution. You may not need a global state at all. For example, if your application consists of only a couple of components. Or if you don't expect that you’ll have any data that will have to be accessible in the whole application, like user personal information along with his security role or token. Using another tool always requires more boilerplate code and often introduces more complexity.

Want to see Redux in action? Check out the Treasure Earth case study!

What is Flux?

Continuing to explore the basics, we have to talk about Flux. Back in time, Facebook developers were fighting with “zombie notification bug”. They realized that this bug comes from current architecture. Basically, there were so many components linked to each other and dependencies between them, that this has introduced non-deterministic results. This observation has resulted in the creation of the Flux pattern. 

Yes, exactly - Flux is not a library, nor framework. This is the architectural pattern on which the Redux and later Vuex (with some diffs) were based. Because of the similarity and of the fact that MVC pattern is widely known by developers, Flux is often confused with the model-view-controller pattern. The main difference here is about data flow. Flux assumes unidirectional data flow, while MVC is based on bidirectional flow.  Basically Flux pattern consists of three major parts - the dispatcher, the stores, and the views (usually components). 

redux vs vuex flux

Unidirectional data flow is the most important thing in Flux. Let’s analyze the above diagram and data flow step by step. The first thing that starts everything is an action. Actions are objects created by action creators, helper functions, which based on provided parameters creates an action. An Action consists of action type and eventually action payload, that is the data that will be propagated to the store. Next in the queue are dispatcher and store. The Store is a “single source of truth” - it’s a global state of our application. It's the central point of the Flux pattern, where data are stored and passed to all components. So, the dispatcher takes an action and dispatches it to a store, which updates themself and propagates a change event to views. This ensures that the view layer always has the newest data from the store. 

What is Redux?

When our knowledge about the idea and basics is sufficient, we could proceed to more interesting things. Redux is a lightweight library, mainly for managing application state. Redux was created by Dan Abramov in 2015 (who later got a job in Facebook), based on Flux pattern and heavily influenced by Elm language. The influence of Elm and the functional programming paradigm is mostly visible when it comes to changing state. The main rule is “never mutate state directly”. When there is a need to update state value, you have to provide an entirely new state rather than mutating it. 

Redux, Context, or Local Component state? Find out more state management solutions for React!

But we have to say that Redux is not a 100% implementation of Flux - there are two significant changes, which are worth noting. First one, that Redux assumes a “single source of truth” while Flux can have many stores. Redux always contains a single root reducer. You might wonder what if the API exposes combineReducers() function. But as the name suggests, this function takes many reducers and merge them into one, root reducer. But what is a reducer? A reducer is a function that accepts an accumulation and a value and returns a new accumulation. Just like in plain JavaScript, an Array object has in its own prototype function reduce(). In Redux, the accumulated value is the state object, and the values being accumulated are actions. Reducers calculate a new state given the previous state and an action. Because reducers are functions it’s not hard to guess that functional programming paradigms are present here too. Reducers must be pure functions what means they have to always return the same value for given arguments and be without side effects, like mutating objects. 

But if the whole concept is based on FP, you might think about how to deal with async and side effects of actions. What if I need to do some conditional, asynchronous side effects in action? Here comes the big advantage of Redux, its extensions, or - middlewares. Middlewares are optional libraries that can be used to extend Redux functionality. To achieve asynchronous API calls from actions, you may choose Redux-thunk or Redux-saga, but in fact, Redux has hundreds of high-usable middlewares. Most popular are listed on Redux docs.

What is Vuex and how does it compare to Redux?

Vuex emerged similar to Redux. About half a year after Redux was introduced, at the beginning of 2016, work on Vuex started. Vuex is also based on the Flux pattern, but just like in Redux, it does not follow the pattern complitely. Like in Redux here the “single source of truth” principle is present - one app, one store. Vuex also does not have a dispatcher, which is present in Flux. Both Redux and Vuex deal with state-changing not like Flux assumes. In both cases, the responsibility of the dispatcher is flipped to the store entity. And there is the main difference between them - while Redux uses reducers Vuex uses mutations. In Redux state is always immutable, while in Vuex committing mutation by the store is the only way to change data. Let’s see that difference in the picture. 

Redux reducer example:

const initialState = {
failureCause: '',
isFetching: false,
jwt: null,
userProfile: {},
};

export default (state = initialState, action) => {
switch (action.type) {
case GET_USER_PROFILE_SUCCESS:
return {
...state,
failureCause: null,
isFetching: false,
userProfile: action.payload,
};
case LOGIN_SUCCESS:
return {
...state,
failureCause: null,
isFetching: false,
jwt: action.payload,
};
default:
return state;
}
};

Vuex mutations example:

export const state = () => ({
failureCause: '',
isFetching: false,
jwt: null,
userProfile: {},
});

export const mutations = {
GET_USER_PROFILE_SUCCESS(state, payload) {
state.failureCause = null;
state.isFetching = false;
state.userProfile = payload;
},
LOGIN_SUCCESS(state, payload) {
state.failureCause = null;
state.isFetching = false;
state.jwt = payload;
},
};

The above code snippets have exactly the same functionality and structure - both do the same thing. The first thing that gets noticed is of course the approach to change. In the first snippet, we can see that nothing is mutated. Instead, we’re providing a copy of the state using the spread operator and then we supply new object parameters, which come from arguments. In Vuex however, we see mutations. We’re directly modifying the state with a simple assignment operator. 

Vuex vs. Redux - what to choose? 

There is no direct answer to this question. From this article, you can learn that both of the discussed state management tools are based on the same pattern with small differences between them, both are framework agnostic (what means that you can freely use Vuex with React and Redux with Vue) and both of them are very powerful. 

Some people could notice or know that Redux demands more boilerplate code to work. Mutations are simpler than reducers. Also, actions in Redux are more verbose, as you need to write action creator which will be dispatching proper action object to store, with type and payload, while in Vuex you need to write one function which will be responsible for e.g fetching data and committing proper mutations. The choice is yours and depends on your project - I hope my article made it easier to choose the right solution for you!

Do you like how we work? Join our frontend team! Take a look at job offers and apply! 
 

Navigate the changing IT landscape

Some highlighted content that we want to draw attention to to link to our other resources. It usually contains a link .