1. 17
    Refactor a React Astro Island to Vanilla JS to Ship Less JavaScript
    7m 57s

Refactor a React Astro Island to Vanilla JS to Ship Less JavaScript

Lazar Nikolov
InstructorLazar Nikolov

Share this video with your friends

Send Tweet
Published 5 months ago
Updated 3 months ago

One of the beauties of Astro is that you don't need to use a JavaScript UI library. Astro lets you us plain JavaScript when you want to.

It's a good idea to get into the habit of starting with vanilla JS when you have a simple use-case like adding a like button. This will mean less JavaScript on the page for your user than if you were to implement this in React.

We'll refactor our React like button and turn it into vanilla JS with the result for the user. Another added benefit of this approach is you'll get more familiar with the native browser API which will pay dividends for you in the future.

Lazar Nikolov: [0:00] In the previous lesson, we learned about Astro items and how to use React to build interactive components in our website. Astro allows us to control when we want to load and hydrate the component, but we still need it to load React's runtime for it.

[0:15] In this lesson, we're going to refactor the PostLike component into Vanilla JS to prove that for simpler components, we don't actually need to use any UI frameworks.

[0:26] Let's begin. Let's start by removing the React integration in our Astro config file. Then we can open the package.json file and uninstall the React packages.

[0:38] That's astro.js React, types React, types React-dom, React, React-dom, and those are it. Let's also open the TS config file and remove the whole compiler options property. There we go, React is officially uninstalled.

[0:53] Since we're going to be working with an ordinary Astro component, let's rename our PostLike component from postlike.tsx to postlike.astro. We're going to remove the export function and the return, and we'll also convert the class name to just class.

[1:10] We're also going to add the component script at the top. If we run this, we'll see that we have an error in the blog slug page. Let's open that and fix it. Since we converted this to an Astro component, we need to append the .astro next to the import. There we have it.

[1:27] To make this component interactive, we're going to use plain JavaScript. To add plain JavaScript to our page, we need to add a script element at the bottom of the page, like so. To make our button clickable, we need to create a reference to it and add a click event listener.

[1:44] First, let's give our button an ID, and let's set the ID to Like. Then inside of our ID as an argument. This will return the reference to the button component. Let's save it as a Like button. There's our reference. Now we can add an event listener. We can do Like button, addEventListener, and we're going to listen on the click event.

[2:12] Inside of the callback, we're going to invoke a function named hit-like. Let's define that, function hit like, and inside, we can bring the alerts back again, "Hello from hit like." Let's try clicking the button. Now, there's our alert, which means our component is interactive.

[2:30] One thing that we need to change is go back to the blog slug page and remove the client directive. Our component will still work because Astro will always load the contents of the script element into the browser. We can make sure by opening the Inspect Element and taking a peek at the head tag. There it is. There is a script that points to the PostLike.astro file.

[2:54] Let's complete our component. When we click on the component, we would like to increment the number of likes of the post that's currently shown. That means that we probably want to display a number of likes label to display the current number of likes and perform a fetch to our imaginary API when the button is clicked.

[3:11] Let's do that. Let's wrap the button with a div and add a paragraph next to the button that displays likes. We're going to add a span with the ID number of likes, or num-likes, and we'll say zero inside. We're adding a span so we can reference the actual number and change it later. There we go. Below the button, we have likes -- zero.

[3:35] Let's create this reference now. Below the Like button, we can do number of likes equals document.getElementByID(num-likes). Now we can delete the alert and just simply get the number of likes and change the inner text to the current inner text plus one so we can increment it.

[3:56] If you hover on number of likes, we're going to see that the number of likes is possibly null. In order to fix that, we can just wrap it with an if-statement -- if number of likes exists, only then set the inner text.

[4:08] We have another error that the type number is not assignable to type string, which means we need to convert this into a template string. Now when we click the button, we're going to see that the likes are always incrementing.

[4:23] We do need to call an API to increment the likes. First, we need to know which post this component refers to. Are we incrementing the Astro Rocks, or are we incrementing the Hello Egghead post?

[4:37] The easiest solution is to define a slug property in our props. Let's scroll up and define our props interface. We're going to accept a slug which is going to be a string. We can destructure the slug from astro.props. We can't just use this slug down in our scripts, we need to find a way to pass it. To do that, we can add it as a data attribute to the button.

[5:03] After the ID, we can add a new attribute called data-slug, and we're going to set the value to the slug from the props. The data attributes of every element can be accessed through the data set property. We can scroll down to the Like button and create a new slug variable that accesses the Like button, the data set property, and the slug value.

[5:28] This is how we can pass the slug down to the script. We can now go back to our Blogpost page and pass this slug as the post.slug.

[5:38] Now, we have the data to send the requests instead of the hit like, let's add a fetch method. We're going to create the endpoint as a template string with the value of /API/likes/ and then we can pass this slug. Of course, the method is going to be a POST.

[5:55] First, we're going to try to unpack the JSON result. Then we're going to grab the data and bring the number of likes if statement that we had previously inside. Instead of parsing the previous value and adding one on top of it, we can just say data.likes.

[6:15] In case an error happens, which in our case, it's always going to happen since we don't have an actual API, let's show an alert with the text, "Couldn't Like the Post."

[6:27] If we open the inspect element, open the network tab and click on the button, we're going to see the alert, "Couldn't Like the Post." That means it entered the catch block here. Before that, it actually sent a POST request to /API/likes/Astro-rocks, because we are currently viewing that article.

[6:48] We'll define the API in one of the lessons later. Right now, we have an interactive component that doesn't need a UI framework to function. The behavior is the same, but we're not loading any runtimes and libraries to achieve it.

[7:02] Now let's do a recap. Plain JavaScript is sometimes good enough to achieve functionalities like sending an API request to fetch data, or changing a text of a certain element. We learned that we can have client-side JavaScript in our plain Astro components by adding that in the script tag.

[7:19] Astro will compile this JavaScript for us, and link it in the head element of our page. We also learned that we can pass through Astro props by using the data set property. We used the document.GetElementByID method to create references to our elements, and then added interactivity with the addEventListener, fetch, and alert methods.

[7:41] Our component was simple enough to do in plain JavaScript. You'll find that that's the case for lots of other components as well. Remember to avoid loading unnecessary JavaScript use frameworks like React sparingly only when you really need to.