fbpx

Blogs from the Ranch

< Back to Our Blog

Learning Vue from React

Avatar

Julian Kyer

Learning Vue from React

Picking up a new framework or library can seem intimidating. The good news is that React and Vue are two popular, well-supported tools for front-end development, and if you already know one, you can leverage your knowledge to become productive with the other relatively quickly.

Both are JavaScript-based, use a virtual DOM, and use composable, reactive components to build applications. They both have CLIs that take care of the boilerplate required to spin up an application quickly and easily. This blog is written with React developers learning Vue in mind, but if you are a Vue developer learning React, stick around. Many of the big, practical differences between the two come down to syntax and knowing one of the two can be easily leveraged into learning the other.

By exploring two applications, one written in React and one written in Vue, that output identical content in the browser, compare how they accomplish the same goal (both were scaffolded with their respective CLIs, Create React App and Vue CLI). Both applications make a request to an API that returns an array of music genres that will be rendered into a list of cards. You can find repositories for the applications below if you want to clone them and experiment, or you can just read along with the code snippets. Both applications are admittedly heavy-handed solutions to the problem presented but should give enough context to understand the basics of both technologies.

App

sample app screen with genres of musicThe main entry point for both applications is the <App /> component. Comparing the React and Vue <App /> components, it may not be instantly clear that React uses JSX to create component structure, while Vue uses HTML. This will become more apparent as you move along.

What does stand out is that not only does the Vue component import the child components it needs, it also registers them in the exported object in the script tag. If you are a React developer and forget to do this, you will get a big, nasty error. Vue registering components this way allows child components that do not need updating to avoid a re-render.

// App.js
import React from "react";
import GenreList from "./GenreList";
import "./App.css";

const App = () => (
  <div id="app">
    <h1>Genres</h1>
    <GenreList />
  </div>
);

export default App;
// App.vue
<template>
  <div id="app">
    <h1>Genres</h1>
    <GenreList />
  </div>
</template>

<script>
import GenreList from "@/components/GenreList.vue";

export default {
  name: "App",
  components: {
    GenreList
  }
};
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

By convention, Vue also provides functionality identical to what something like styled-components provides for React. By adding the scoped attribute to the style tag in a Vue component, a unique ID is added to the component that limits the styles to that component only. Both CRA and Vue CLI allow simple setup for using SCSS instead of CSS.

GenreList

<GenreList /> does the heavy lifting of the application, by making your API call, managing the state of the application, and rendering a list of genre cards. Both applications import an identical genreService function that provides the data for the component. Each application sets the response to their internal state management and then iterates over the array of genre objects to render genre cards.

In the React version, you make the API call inside of a functional component with the useEffect hook (this could also be accomplished in a class component with the componentDidMount lifecycle method), and update state with the response data using the useState hook (in a class component, you would call setState on a successful response). You can then map over the genres array in state and render a <GenreCard /> for each genre.

// GenreList.js
const GenreList = () => {
  const [genres, setGenres] = useState([]);

  useEffect(() => {
    genreService
      .getGenres()
      .then((response) => {
        setGenres(response.data);
      })
      .catch((error) => console.log("There was an error", error));
  }, []);

  return (
    <div className="genre-list">
      {genres.map((genre) => (
        <GenreCard genre={genre} key={genre.id} />
      ))}
    </div>
  );
};

Vue 2 doesn’t have the concept of functional versus class components (although Vue 3 will). In GenreList.vue, you use the mounted lifecycle method to make your API call, and you store it in data, which is analogous to React state. In your data description, you give genres a default value of an empty array so that you don’t have any problems when iterating over genres before the API call resolves.

// GenreList.vue
<template>
  <div class="genre-list">
    <GenreCard v-for="genre in genres" :key="genre.id" :genre="genre" />
  </div>
</template>

<script>
import GenreCard from "@/components/GenreCard.vue";
import genreService from "@/services/genreService";

export default {
  components: {
    GenreCard
  },
  data() {
    return {
      genres: []
    };
  },
  mounted() {
    genreService
      .getGenres()
      .then(res => (this.genres = res.data))
      .catch(error => console.log("There was an error", error));
  }
};
</script>

<style lang="css" scoped>
.genre-list {
  padding: 0 15%;
}
</style>

The syntax for iterating over a list in Vue is a bit different than React. Since you are using HTML instead of JSX, you don’t have access to the full power of JavaScript in your template, so Vue uses directives, or special attributes in the markup, that act as instructions to Vue on what to do to the DOM. In this case, you use the v-for directive to tell <GenreList /> to render a component (or HTML element) for every element in a list. Additionally, you can’t use curly brackets to pass props to child components in Vue, so you will need to use v-bind directives to pass props to a child component. This can be written as:

<li v-for="item in items" v-bind:key="item.id" v-bind:item="item">

or

<li v-for="item in items" :key="item.id" :item="item">

Both do the same thing, the second is just a shorthand syntax.

GenreCard

The last component to review in this application is <GenreCard />, which takes a genre object as a prop and renders the data from that object.

In React, <GenreCard /> is a functional component that destructures props on the way in and renders a simple card with a genre and a description.

// GenreCard.js
const GenreCard = ({ genre }) => {
  return (
    <div className="genre-card">
      <h4>{genre.genre}</h4>
      <p>{genre.description}</p>
    </div>
  );
};

export default GenreCard;

The Vue implementation of <GenreCard /> is similar, although you are required to describe the props the component expects to receive. This is very similar to React’s prop-types, although it is not optional in Vue. Vue also uses double curly brackets, called “mustaches,” to inject data into a template, as opposed to React’s single set of curly brackets.

// GenreList.vue
<template>
  <div class="genre-card">
    <h4>{{ genre.genre }}</h4>
    <p>{{ genre.description }}</p>
  </div>
</template>

<script>
export default {
  props: {
    genre: Object,
  },
};
</script>

<style lang="css" scoped>
.genre-card {
  padding: 20px;
  margin-bottom: 20px;
  border: 2px solid black;
}
</style>

Wrapping up

If you’ve been using React or Vue in your development, hopefully this blog has encouraged you to try something new that you aren’t familiar with. Differences are to be expected, but these two technologies are very similar in terms of philosophy, implementation, speed, and ease of use. Developers shouldn’t be discouraged from moving between the two. In fact, working with both should be encouraged, because it makes developers more versatile and expands the range of projects they can work on.

Avatar

Julian Kyer

Not Happy with Your Current App, or Digital Product?

Submit your event

Let's Discuss Your Project

Let's Discuss Your Project