Refetch API data when a state value changes with the `$watch` property in Alpine JS

Share this video with your friends

Send Tweet
Published 2 years ago
Updated 2 years ago

In this lesson, we build a little app that fetches dog photos from the API, based on a "breed" search field. We want the API call to happen again when the search value changes, and to do so we use the $watch property from Alpine JS, which lets us define what state property we want to watch, and what callback function to run when a change happens.

We also use the debounce modifier on the x-model property to avoid firing an API call on each keystroke.

Simon Vrachliotis: [0:00] Here's a fun little app that fetches a random image of a dog based on the breed that is typed in the search field. If I type corgi, sure enough, we get a corgi. When a search doesn't match a breed, we get an error message. If the search field is empty, we get a prompt to search for a breed.

[0:17] This is the final version of the app, but for this lesson, let's assume we are halfway through the implementation, and a few things are not working properly. Here's our current work in progress. The initial state for the breed is set to corgi. We can search for another breed, labrador, but nothing happens. No further API calls were made. Let's look at the codes to understand what's happening.

[0:41] You can see in x-data that the component has been extracted to a function, since there is a bit of logic going on here. Our component returns a breed initially set to corgi and used as the search value in the input field.

[0:55] We also have a fetchStatus state which allows us to toggle between the loading spinner during API calls, an error message if no breed was found or, if all goes well, display our dog image. We also have a data property, which is where the data from the API response will go.

[1:12] Finally, we have a fetchDogs method, which is where the API call happens. We fetch a URL which contains the breed value, and if all goes well, sets the response data in our data state. If we scroll to the top here, we can see that on init we are making a call to fetchDogs with our initial breed of corgi, which is why we get the initial image here.

[1:35] When we search for another breed, the breed value updates in the states, but nothing tells fetchDogs to run again. What we want is our fetchDogs method to listen or watch for a change in the breed value and rerun when a change happens.

[1:50] Alpine JS provides the $watch property, which lets you watch a component property and fire a callback whenever that property changes. Let's change our x-init to instead of calling fetchDogs, call a new method called init. Down in our components, we'll define this new method init. In there, we'll set a watch with $watch.

[2:14] Watch takes two arguments, a string for the property to watch and a callback to run whenever the property changes. Our string here will be 'breed'. When that changes, we want to run fetchDogs again. Let's try that out.

[2:34] Looks like the app is now broken. We are stuck in the loading state and no API call was made. If I type a search, we can see that the watcher works, the fetchDogs method is called and we get an image.

[2:46] Turns out, we told the app to fetch dogs when the breed changes, but nothing calls the fetchDogs method on init anymore. We can fix that by adding an initial call to fetchDogs in our init method like so. Voila, now the app works properly.

[3:03] There is one annoying problem though. When we type a search, an API call is fired immediately on each keystroke leading to a lot of errors and wasted network requests. We should probably change that and perhaps debounce the search field.

[3:17] Surprise. Alpine JS lets you change the x-model directive with the debounce modifier and then sets the duration for it. Let's go with 750 milliseconds. Now, if I switch from corgi to labrador, we only made one network request after I stopped typing. Let's do one more search. Yep. Only one request.