Doing Vue after three years with React

I was working with React for the last few years, first separately, then together with Redux and other libraries from the React-universe (react-router, react-redux, prop-types, etc.). I liked how simple and convenient it was and my experience with React was always a pleasant one. I am happy to live in times when we have lots of great tools to help us develop applications better and faster.

For the last three months I was building web-app with Vue and I want to share my experience of dealing with Vue after being a devoted React developer for a few years. It is not intended to be yet another Vue/React comparison post, there is a big amount of such comparison available, including official Vue documentation. Meanwhile it is more of a personal perspective of switching from one library to another.

I hope this will be useful for people who are dealing with both Vue and React, or who, just like me, are getting accustomed to Vue after React, or just want to know more about both.

Somewhere between React and Angular

Sometimes Vue is compared to Angular even more then it is compared to React. Indeed, the first thing that you see while scrolling through Vue templates are two way bindings and directives, that look very familiar to the ones that Angular uses:

<div id="app-3">
<span v-if="seen">Now you see me</span>
<ol>
<todo-item
v-for="item in groceryList"
v-bind:todo="item"
v-bind:key="item.id">
</todo-item>
</ol>
<button v-on:click="reverseMessage">Reverse Message</button>
</div>

Although Vue supports JSX, the established approach is still to separate JavaScript from markup. And while in React’s JSX syntax is pretty much native and reflects the usual JavaScript syntax, the template’s syntax in Vue is very advanced — it includes directives, shorthands and conditional rendering, which make Vue look like Angular, but be aware that it is pretty much where the similarity ends.

Of course, it might be a great advantage to be able to use the same templates both for Back-end (node.js/Pug) and front-end (Vue/Pug) and the number of directives that Vue provides might be useful, but still, for me switching from React’s JSX to Vue’s templating was (and still is) painful.

Redux vs. Vuex

Usually for applications React is used with some kind of data-flow library and the most popular choice is Redux. There is a similar data-flow library for Vue called Vuex, and I was happy to discover that it’s is very similar to Redux. Indeed, switching from Redux to Vuex was practically painless, since two libraries have much more in common, than React and Vue themselves.

The main difference is that Redux heavily relies on immutability of the state. It comes from a fact that Redux was made with React in mind, and even though React itself might deal with mutable data, it is recommended to avoid mutating values that you are using as props or state in order to make React work in the most optimised way.

In React, when a component’s state changes, it triggers the re-render of the entire component sub-tree, starting at that component as root. To avoid unnecessary re-renders of child components, you need to either use PureComponent or implement shouldComponentUpdate whenever you can. You may also need to use immutable data structures to make your state changes more optimization-friendly. https://vuejs.org/v2/guide/comparison.html#Optimization-Efforts

While Vuex doesn’t care about immutability of state at all.

In Vue, a component’s dependencies are automatically tracked during its render, so the system knows precisely which components actually need to re-render when state changes. https://vuejs.org/v2/guide/comparison.html#Optimization-Efforts

Hence come a few differences in a way that React/Vue components interact with a store and I would like to talk more about them.

Dispatch and Commit

The data flow in Redux in pretty strict and straightforward. Component dispatches the action, which is returned with the help of action creator function. Then the reducer returns new state according to the action received. And in the end component is listening to state changes from the store and is able to access state properties with the help of connect() function.

Every action goes through action creator. And even though in theory it’s possibly to dispatch action directly from component, it’s rarely the case. The syntax of action itself encourages us to encapsulate the logic in action creator functions, even in the case of most trivial actions:

import { ADD_TODO, REMOVE_TODO } from '../actionTypes'
function addTodo(text) {
return {
type: ADD_TODO,
text
}
}

While the data flow in Vuex is pretty much similar, it’s not so strict on the number of ways that component can interact with a state. First, component can dispatch action. They are usually used to do async stuff like fetching data from back-end. After that, action commits a mutation. Mutation function similarly to reducer is the only one who is allowed to change the state. But there is another way around — the component can commit a mutation directly, and it’s very tempting sometimes to reduce the data flow by ditching actions completely.

It’s not only that committing a mutation from component is not strictly forbidden, but Vuex documentation even encourages to use actions only in cases of asynchronicity. Coming from more strict React data-flow I believe in more strict separation of concerns — committing a mutation should come from action only, even though it’s sync or completely trivial.

If we allow a component to create a mutation through action only, there always will be an additional layer between component and mutation keeping coupling between them low and in the end making code easier to maintain and change.

Getting data from the store

To be able to interact with a store inside React components one should use connect() function. And I think one of the most frustrating things in whole React/Redux data flow is making a decision for which components exactly the connect() function should be used. Components where connect() is used are usually called containers opposite to the simpler once called presentation or “dumb” components.

You can’t use connect() too often, because it’s quite heavy, though if you will use it only for top components you will get lost in number of props that you will have to pass down to lower accessors. The problem is discussed here and here, but really, even with a reasonable amount of container components the number of props that you pass down down still remains quite painful.

Keeping this problem in mind I was pleasantly surprise to discover, that I don’t even have to think about it with Vue. The store is accessible in every Vue component and that is as simple as that.

const Counter = {
template: `<div>{{ count }}</div>`,
computed: {
count () {
return this.$store.state.count
}
}
}

Which means that the number of props that you have actually pass from one component to another is quite limited and required only for data, that are not kept inside the store. It’s a blessing, considering, that passing props in Vue has a very unconvenient syntax:

<template>
<div>
<todo-item :todo="todo"></todo-item>
</div>
</template>
<script>
import TodoItem from './TodoItem.vue'
export default {
components: {
TodoItem
},
data () {
return {
todo: {
text: 'Learn Vue',
isComplete: false
}
}
}
}
</script>

Here we pass props to the child component (TodoItem) not when it is declared, but much further, inside the template. Meanwhile, in React it happens in one place in a more natural way — right when the child component is rendered:

class TodoList extends React.Component {
render() {
<div>
<TodoItem todo={this.props.todo} />
</div>
}
}

Even though passing the props in Vue is more inconvenient, the good thing is, that due to the fact that store is reachable in every component, the number of props that you actually have to pass is pretty insignificant compared to React, where even with a good amount of container components number of props that the average component receives is quite large.

Update: There is a new React Context API that provides a way to pass data through the component tree without having to pass props down manually at every level.

Conclusion

As I mentioned in the beginning this article is just a number of observations that I found most significant while transitioning from React to Vue. It’s not a strict comparison, that can be used to choose one library over another. But if you, like me, have to switch from one to another or just look for additional information on both libraries, it might be useful.

TL;DR

  • Vue doesn’t come with JSX by default and is pretty strong with idea of separation of scripts and templates
  • Redux and Vuex have a similar idea of data flow behind them
  • Redux heavily relies on immutability of the state, while Vuex doesn’t care about state’s immutability
  • In Vue it’s possible to both dispatch and commit directly from the component, but it’s better to keep it more strict and allow dispatch only
  • Store is directly accessible inside every Vue component

Thanks for reading this article. Please leave claps if you find it useful! And I will be happy to hear any feedback.