Why React instead of Vue?

Which path
Erik
Erik, Technical Director
As a programmer, especially in the world of front end development, you are constantly tempted to choose the most popular technology. My job as a Technical Director is to make an informed decision on what technology to use. This article is about the choice we’ve made between React and Vue for Kaliber. We describe the benefits, differences, and considerations for both. Time for a deep dive!

Publicatie: 29 september 2020

File structure

Vue has the concept of ‘Single File Components’, I really like this. It means that your style, html and javascript live in the same file. React has (by default) the html (JSX) and javascript in the same file but style is separate (we use CSS modules). We could get the styles in the same file with React by using ‘CSS in JS’, but that has some disadvantages when it comes to tooling.

Both the ‘CSS in JS’ for React and ‘styles + templates’ in Vue make linting of our CSS and javascript quite complicated and in some cases impossible. In React because the CSS would essentially be javascript strings (interpolated) which makes it hard for tools like POSTCSS. In Vue because the relationship between styles and html is a bit more complicated.

If I could make a wish it would be to have the CSS in the same file as the React component without crippling any of the tooling (eslint, typescript for javascript, style-lint, vscode smartness, postcss, babel, …).

Templates vs JSX

Before we dive into Vue vs React on this one, first a little background on templates. Back in the day you had the 3 big ones to make websites: PHP, ASP (before .NET) and ColdFusion. These programming languages all had one thing in common: they were general purpose languages with first class html support. They all could do something like this:

1<h1><%= title ></h1>

This made it really easy to generate html but there was no limitation on the code that could be executed in those templates. This caused significant maintenance problems because you could for example easily query the database in those locations making the pages very slow.

To solve these kinds of problems template languages were created. They forced people to prepare the data before it was rendered into output. A welcome benefit of having a separate language for templates is that this made it easier for frontend developers. They only needed to learn the template language and not the whole backend language.

While it forced some great conventions, it also gave rise to problems that we didn’t have before. At some point in time you will reach the limits of the template language and are forced to do complex view logic outside of your template or extend your template language to support the feature you want.

As a company we have had this complete journey in big production systems and we are now back where we started: general purpose languages with great html support. We don’t regret using a template language as we learned a lot from it and it educated our developers. However, we realize that having a general purpose language at our view layer prevents problems and does not cause problems as long as you have strong conventions.

Vue uses the Vue template language by default and React uses JSX (syntactic sugar to provide tag support in Javascript) by default.

So for us as a company and our place on the learning curve, JSX is the right choice. While possible to use JSX with Vue, it’s not the default. Our experience tells us that, when presented with a choice, using the default of a framework is best: most examples, documentation and presentations will show the default option.

Vue Composition API vs React hooks

Vue and React follow a similar evolutionary path and at this point in time both frameworks have a similar approach to combine and allow for reuse of non-trivial features. In Vue using the ‘composition API’ and in React using ‘hooks’.

Both approaches share the same philosophy and try to solve the same problems. How they achieve / implement this is subtly different. A few of these differences makes me favor hooks over the composition API:

Vue’s setup function runs once, React hooks run each time the component renders.

From an objective standpoint having a setup function means that component properties (that can change at runtime) are harder to use; setup is only called once. In Vue the properties are reactive, which is more complicated than not being reactive. This is part of the ‘Vue way’ of doing things so this is not a problem for actual Vue developers, but it does make some things a bit harder.

In React a hook is executed for each render. This means properties are accessible as normal function arguments. The downside here is that you need some mechanism to ‘only do things in certain situations’, this mechanism is the ‘dependency array’ for some of the hooks. Having a dependency array in itself is not a problem, the problem comes with anonymous functions passed into some hook that might have captured ‘out-of-date’ properties.

You could argue that these 2 approaches also have a difference when it comes to performance, but the winner (performance wise) probably depends on the use case. If you do want to make that argument, make sure you test your claims and supply context.

Another drawback of React hooks is that they can not be used conditionally (or in loops).

Both approaches are the preferred (and best) way to do it if you look at it from the perspective (or philosophy) of the frameworks. If you pile up the pros and the cons of each approach I think the piles end up very similar. This means that the choice is about taste.

I favour the React approach because the hard part is an actual Javascript feature that developers need to learn about (back in the day we called it ‘Activation Object’). The two other ‘quirks’ (dependency array and not conditionally) are easily explained and lintable.

In setup you return the combined (composed) parts, hooks are assigned to variables

In examples of the composition API you often see this pattern: { ...feature1, ...feature2 }. Regardless of Vue, this is a pattern I do not like because it causes problems.

Imagine feature1 and feature2 are living in separate files (because they are reused). feature1 looks like this { x: ‘a’ } and feature2 like this { y: ‘b’ }. So when you compose them the result is { x: ‘a’, y: ‘b’ }. What happens if a developer changes y in feature2 to x? I hope you realize how hard it is to prevent these types of bug-inducing changes, there is no indication in the feature2 file that changing y to x would be problematic. And if you get to the location where feature2 is used there are still no red flags.

This is the same problem we had with mixins. Supposedly the composition API should fix these types of problems: How the Vue Composition API replaces Vue Mixins (Naming collisions…solved!).

Now look at this example:

1export default {
2 setup () {
3  const { someVar1, someMethod1 } = useCompFunction1();
4  const { someVar2, someMethod2 } = useCompFunction2();
5  return {
6   someVar1,
7   someMethod1,
8   someVar2,
9   someMethod2
10  }
11 }
12}

What would you say if a colleague refactored that into this?

1export default {
2 setup () {
3  return { ...useCompFunction1(), ...useCompFunction2() }
4 }
5}

The refactoring seems great, but it can easily cause hard to detect naming collisions in the future.

If you end up choosing Vue, make sure you get some form of linting that prevents this kind of refactoring. The verbose version is the one you need. You should really value this rule: do not destructure into the return object of the setup function.

Hooks in React do not cause these types of problems because there is no need to combine their results into one data structure. You assign the results of hooks to variables in the body of your component and simply use them.

Vue components have the concept of a lifecycle, React focuses on the intended use

Old school developers know the component lifecycle of their favorite framework very well. A component mounts, it unmounts, can be rendered in reaction to a state change, or maybe a property change, …

The Vue composition API still has these concepts. And this means that as a developer you need to be aware of the lifecycle. With hooks React took a different approach. Let me show you an example:

1// Vue
2setup() {
3 let timeout = null
4 onMounted(() => { timeout = setTimeout(...) })
5 onUnmounted(() => { clearTimeout(timeout) })
6}
7// React
8useEffect(
9 () => {
10  const timeout = setTimeout()
11  return () => { clearTimeout(timeout) }
12 },
13 []
14)

I think it’s better when we are not focussing on how the framework’s internals work, but more on what we want to achieve. For example: with effects, I think the hooks version is very good. The first bit is about the effect itself: perform the effect and return (optionally) a means of cleanup. The second part (the dependency array) is about the lifecycle of the effect:

  • useEffect(() => { ... })
    - No dependency array
    - Runs if anything changes
    - Cleanup and install for every render
  • useEffect(() => { ... }, [])
    - Empty dependency array
    - Run once
    - Install the effect as soon as possible and clean up as late as possible (effectively mount and unmount)
  • useEffect(() => { ... }, [foo])
    - An array with dependencies
    - Run if foo changes
    - Cleanup and install when foo changes

Small side note for when your choice is Vue: make sure you do not have multiple lifecycle hooks in a setup function or address multiple features inside a single lifecycle hook. If you encounter that, refactor those bits into composition functions. If you don’t you get the exact same problems that the composition API was designed to solve.

Out of the box features vs freedom / choices

Vue comes with more features attached (think of routing, animation, state management, …) and I like that. Having these ‘essential’ things provided by the framework is a great benefit, both in maintenance and education. That being said, if one of those things is not usable in a certain situation, you will get in trouble.

Every framework has its escape hatches that allow you to use something else. But anyone who has been down that road has learned a few things:

  • It takes time (experience, frustration and knowledge) to realize you should not try to use the ‘out of the box’ thing for a specific situation.
  • When you finally realized you should not use the ‘out of the box’ feature, your code will probably be in a less than ideal state caused by your previous attempts. Also, chances are that the system that you have built is likely non-trivial, otherwise you probably didn’t need more that is provided out of the box. And this means that a big part of the system is already tied to the ‘out of the box’ feature, making a switch to an alternative costly.
  • The learning curve for these escape hatches is very steep, you need to do it the “framework name here” way.

On the other side of the coin you have the complex problem of choice. You are probably not the first one dealing with a certain problem, so: are the other solutions out there any good? Are they safe? Are they well maintained? Do the developers of the solution have a philosophy that fits yours, your company’s or your business requirements?

I don’t think this particular trade-off of out of the box features vs freedom has very much weight in the Vue vs React debate for us. I am not particularly blown away by the extra out of the box features provided by Vue, so even if we used Vue we would probably have the same problems we are facing now.

Migration

We are currently using React, this means that in a comparison with an alternative the scales do not start balanced. Any alternative has the counter-argument of migration: it takes significant effort to migrate applications and developers to the new framework. This means that slightly better is not good enough for an alternative, it has to be a lot better in order to take migration into account.

As a company we have extensive experience with these types of migration. So these types of migration are not something we are scared of, but we do realize the associated cost that comes with it.

Should you choose Vue?

For one group of developers this will be an easy choice. If you are a PHP developer (or company) and use Laravel: yes (if you want to know why, watch the Vue.js documentary). Otherwise you will need to do some investigation of your own. Take into account the kind of work you or your company does, your personal preferences, your affinity with the community and your stance on using something backed by a big organisation. Vue is a wonderful framework with a fantastic community.

Conclusion

We’ve discussed the benefits, differences and considerations for both Vue and React. We hope to have clarified why React is the right choice for us at this point in time.

If you have any questions about sections of this article please ask. If you think I am wrong, start a discussion! And if you simply need to express yourself, feel free to do so.