TypeScript and Vue 3

Refactoring Options API Methods

Ben Hong

Ben Hong

TypeScript and Vue 3

Check out a free preview of the full TypeScript and Vue 3 course

The "Refactoring Options API Methods" Lesson is part of the full, TypeScript and Vue 3 course featured in this preview video. Here's what you'd learn in this lesson:

Ben demonstrates migrating Options API to Composition API. The Composition API is a set of APIs that allows the ability to author Vue components using imported functions instead of declaring options.


Transcript from the "Refactoring Options API Methods" Lesson

>> We've done Options API, these are the core functionality of what most people are using it for. And now it's time to hop on over to the bridge to Composition API. But again, the key thing here is the bridge because we're not gonna go fully into it yet.

We're gonna see kind of the intermediary and sort of learn the progression from options to composition. All right, so let's take a look at our code. Let me go open up restaurant page, okay? So we've learned that when it comes to integrating TypeScript with object API or options API, we can see here we had to do like define component.

We had to do some things to pass it into like the data to kind of make it look right. And then especially when it came to prop, we had prop types, wouldn't be nice things are just little bit easier, a little bit more native, right? What if we want a more standard JavaScript, less of the guardrails that options API provides for us.

And that's really where composition API comes into play. And so, the one thing I have to underscore actually a lot right now, which I forgot to animate to you, is that the composition API does not replace the options API. I know people love Buzzwords and things going viral, and I think there was a blog post where I think someone was claiming it at one point.

So that message did become, it kind of like pervaded throughout, unfortunately. And the options API actually, believe it or not, is something that we can very easily support as the Vue team. Because underlying what we've written, options API is basically one style of writing components. So when people are like, this is gonna go away, it's just a style of writing components.

There's nothing unique about what's going on there because the composition API and the options API are powered by the same core library. So I just need that to be just keep pushing that message, so people don't feel like, no, my options API is going away. No, it's not.

But there is another way to help us solve other problems, which we'll see in a little bit. All right, so taking a look at this, let's start to see what composition API might look like. So one of the things I mentioned with composition API is that it does things a little bit more like vanilla JavaScript.

So for example, here with data we have this function that's returning an object, and then we have computed properties, we have all these guardrails. Let's take a look at what it might look like inside of composition API instead. And so the way that you know you're inside composition API is through the setup lifecycle hook.

So for those who come from Vue 2, you might be familiar with mounted, created, all those lifecycle hooks. Setup is the first one that runs at the beginning now of all the component lifecycle methods. And so what we are gonna do is we are just gonna migrate our options API stuff into composition API just to show you where that progression is, so that then later when we take that next jump, it won't be so jarring.

So first thing first, we're gonna start by setting up our reactive data. And so remember what I said inside of composition API is that everything inside of here is vanilla JavaScript. So in other words, when you set up a const of filter text, that is literally what it is.

It is a constant of filter text, it's not gonna change, it's not gonna be reactive because this is vanilla JavaScript plan. So what do we need? We need to tell it that we need some help from Vue. And so the helper method that we're gonna stick with for this particular workshop is gonna be ref.

So think of a reactive reference, that's what that is. So we say, this is going to be a ref that is an empty string, this tells us that, okay, this is gonna be the equivalent of this right here inside of our data. So that's been moved. Next we're gonna go ahead and let's move restaurant lists.

So shall we? So I'm gonna copy restaurant lists over here, and then I'm gonna const this as well. const restaurantList = a ref that's an array, so this is a reactive array, not array, yes, a reactive array. Okay, finally, we gotta move our showNewForm here. So I'm gonna move that over here and const showNewForm.

And this is not gonna be false, but this will be a ref of false just like that. And so now that we've deleted all this, you'll notice now that we are in a sort of untyped territory again. Because when we look and hover over filter text, you'll see that now we have this new type that's being inherited called a ref.

This is a reactive reference, so think of this like when you were looking at options API and you had data, and methods, and all these specific types. This is now saying that, okay, we know how to handle this specific type of ref, which is a reactive reference within Vue.

And so here you'll see that restaurant lists actually does say, well, actually similarly, let me detect the shape of the objects I'm noticing inside of my array. But we wanna go ahead and say, hey, this is an array of restaurants. So the way that we do this is with typecasting, just like we did before where we say this should be a restaurant array.

And just like that, now we've gone ahead and actually defined our ref correctly. And so this should start to feel a little bit more like TypeScript. Okay, and then going forward, here we have here we have a ref, this is a Boolean, so we're good on this end.

So you notice, though, that none of it is actually showing up as being used, right, saying, it's unused. And that's because inside of the setup function, again we're in vanilla JavaScript land, so we need to be a little bit more explicit. And so we need to actually manually return these specific variables, whereas data just assumed you're going to use them.

In the setup function, you might have some that are private variables, some that you wanna expose. So we want you to be explicit in this particular format of setup when it comes to returning this stuff. So once again, I can return this showNewForm. And now that I have this, everything is now can feed into the options API just like you normally would.

But again, we're learning more about composition API, so let's continue this journey and migrate everything over. So for computed properties, now we have these reactive references. We have a computer helper method, and this is the computed helper method is just like the ref where we're basically saying this is gonna be a computed property that's based on Vue.

So here, let's go ahead and grab this. Let's start with number of restaurants, that's the easiest one. So number of restaurants here is going to be a computed property. So this is the helper computed, and it's going to be a function that returns a reactive variable. Now what you'll first notice though, is that it's gonna yell at us because it say, hey, wait a second, where does filteredRestaurantList exists?

Well, it doesn't because this is not working anymore, we're in vanilla JavaScript land, this is not automatically taken care of for us by options API. But what we do have, we do have this, wait, wait, what am I talking about? That is because filteredRestaurantList is here, and then I'll show you what I'm talking about in a second.

Okay, so I can do const = computed, and then this is a function that returns this stuff. Now once again, we're still in a similar problem though because as we notice, this does not exist, thereforeTherefore it can't find this restaurant list property. But we know restaurant list does exist right here, and so your first thought might be while we're in JavaScript land so easy.

I'm gonna go ahead and just delete this, it should work. It does not work, the reason for that is because reactive references play on basically an object definition syntax for tracking reactivity and proxies, which we're not gonna get into. But basically, when you're using composition API with reactive references, particularly the ref helper, you need to unpack the value.

Because otherwise, it's just a constant you're working with, this is why there's no filter. And so if we add this .value to our reactive reference, you'll notice that suddenly everything opens up. It knows what the restaurant is, it knows what to grab. And so whenever using ref, basically remember that you have this dot value that you need to unpack.

And so for those who are new to composition API though, you might be thinking, that's kind of weird, I don't know if I liked that. Because in options API, it just worked, right, this .restaurant list, good to go. The thing about it is though, because you're in sort of vanilla JavaScript land, one of the benefits that the .value actually affords you is that it does make it very explicit to developers that you're working with a reactive reference.

Because otherwise, if you use the reactive helper method, which I'll just show you real quick. So if we say like const todoItem = reactive, so this is another helper method where it might be like id: '123'. Although actually, let me just show you the text pastry. Just as a reminder, we can use that and just scaffold that, and then title: 'Get milk', right?

The reactive object does allow you to just go todoItem.title, and then it just works. But if this were a ref, you do need to do value.title. And so some people will go, well, then I'm just gonna use reactive, and that's totally up to you. Again, I've seen people go both ways on this, it depends on you and your team.

I'm using ref just because I think that's the one that tends to catch people off guard when they're not used to using it. So I want people to just get a little bit more exposure and so that when you see it, and you're working with it, you're not confused.

But by all means, if you wanna just use the reactive, that's totally up to you as well. Okay, so now that we have that, we have this other issue, right? Filter text doesn't exist. Well, that makes sense because we need to eliminate this and just add .value that works.

And once again, delete that .value. There we go. So, now our filteredRestaurantList computed property does work as expected. And you can see here that it actually tells you that, hey, this is actually a type of computed ref. So this is what's really nice about getting into composition API when you're working with Typescript is because you're sure to get a lot more specific as far as how things are being used.

Whereas an options API, it does depend on where you put it in the code, and then you kinda have to like trigger it a little bit to work the way that you want to with options. This feels a little bit more like natural TypeScript because again, it's just a computer helper method, and then you're defining a callback function inside of here.

And then you're basically defining the return here, which is the list of restaurants. And then basically, you're good to go. All right, finally here, for a number of restaurants, we have a dependency on the computed property. Now remember what I said, refs require you to unpack it. So when we look at filteredRestaurantList, it is a computed ref.

In other words, we actually do need to unpack this as well. So this is where .value also needs to come into play. And that's a little gotcha that some people don't realize when you're learning about ref and reactive Is that because computer is using ref, it also needs to be unpacked.

So then it's saying it's not being used, so let's go ahead and add that real quick. So let's see. filteredRestaurantList and then wait, did I miss type it? I think I did. There you go. And then we have the number of restaurants, okay? Great, all right, that means computed has been successfully migrated.

Now we're gonna go ahead and finish this up, or actually with two more steps. Let's go ahead and move our methods in. So methods, right, they got this special name when it came to options API, but really, all they are in JavaScript are functions. That's really all they are.

So I'm just gonna grab all three here and use a multi cursor editing to save us a little bit of time. And so here, let me just delete that, delete that, okay? So here one, two, three, so we have a const of these three functions, we will declare it like this, and then we'll make it an arrow function.

And then already basically these are the functions that we're going to be calling, although this got deleted somehow. And then let's go ahead and fix all this up. So there we can delete restaurantList.value switch. hideForm is gonna refer to this hideForm, so there's no point for this. And then once again, we can grab that, switch it out to .value, this also .value.

Great, and so you see here actually, this is, again we're TypeScript can be super helpful is that without the .value if you forget to unpack it. You can see already that it says that I don't know what I'm unpacking here, there's an any here for some reason. And so that yelling at me reminds me I need the value.

Great, and then once again, showNewForm is just a ref. So there we go. And then we need to expose all this stuff, so deleteRestaurant hideForm. And then the last one was addRestaurant, okay? Finally, one last bit as we migrate this over is that we need the ability to call the mounted life cycle hook because we're again, I wanna show that transition over.

So one of the things we've done as a convention inside of Vue is that we provided you helper methods, right? That's what we've been doing this whole time, ref computed all that stuff. So we have this thing called, basically life-cycle hooks, so onMounted which is a life-cycle hook that we can import as you can see here, it's being imported directly from Vue.

This is what actually allows us to then call those lifecycle hooks. So onMounted here, and then we basically pass in the callback function that will run on that specific one. What are we gonna do? Okay, so this is where some people get tripped up because this is where we now sort of enter the land of composable.

If you haven't heard that term before, basically it's a unique term to any sort of composition API module. So anytime someone uses a function that creates like an example like router, any sort of function, it uses composition API. They're currently being termed as composable because they're external modules that use composition API.

I don't know, I have mixed feelings on the name, but it seems to be sticking, so that's what it is. And so in this particular case, you'll notice that onMounted what we're doing here is we're checking for the route. And if the route has a query parameter of new, we're actually showing the new form basically.

So it's like a hack over rather doing direct routing. So how do we actually get this, right? Because now there's no more global properties. This is one of the things I think that scares people a little bit when moving from options API to composition API is that there's a lot of guardrails that options this gives you automatically, especially when it comes to globally inherited stuff.

And so the way this works actually is that, especially with Vue 3 going forward, with all the libraries being TypeScript friendly, and we wanna be composition API friendly. In addition to being options API friendly, is that we have this prefix in composable that's being used a lot, inadvertently called use.

So what we can do is, since we wanna grab the link from the route, we wanna grab something from Vue router. So if I scroll up here, I can import something called useRoute from vue router. And so if you're thinking of like, well, how would I know to use like use route?

We try to actually follow conventions of what you would expect from the global API. So sometimes you might want router things, so that will be used router, right, if you see using this .$ router. So use routes here is what's gonna basically allow you to get all the things that you would normally get from this .$.route.

So if I do useRoute like this, this will actually then grab the route for me. And then I can swap this out now which is route, and then I can swap this out with .value. And that is everything when it comes to migrating to the composition API. And so as we can see now, we can actually delete this part here.

And so we have this nice, isolated module of JavaScript that's leveraging TypeScript, and then it's done in a way that can be freely organized. And this is what composition API really does offer in comparison to options API is that now when we're looking at this code, we can go, all right, so this is managing restaurants, right?

So in the past, right, this is essentially the methods section. This was the lifecycle section, and then we had computed here. And then we had all our data here. But because we're in free form composition API then, we can actually start thinking like, you know what, actually, I think it makes more sense to group all like the restaurant stuff together.

So I'm gonna take the restaurant here, group that with this, cuz I think this makes more sense that we established the ref. First, I'm gonna call this Restaurant Module, we have the list of data, we filter that data, and then we can even manage that data. So I can do like that.

And then number of restaurants actually is relevant, so we'll keep it there. But then we have all this form stuff, right, and this stuff we can really just say this is the New Form Module. And then we can just say, well, for now the life cycle hook is related to this if not doing anything else different with it.

So we'll grab that, and then we'll grab this to stuff too here. And now we have stuff organized by feature rather than by their concerns or how they're functioning. Is there a better best practice? No, it is entirely up to your team. However, as you might have noticed though, because we're in vanilla JavaScript land, this does open us up to then creating the composable.

In other words, we could export these chunks of code into individual JavaScript or TypeScript files, and then share them amongst components. And that's where it starts to get really powerful with composition API is that reusability piece. Because with options API, you were limited to using essentially mixins, which was prone to errors as far as like naming things, and then you have to worry about things overriding a certain way.

But because everything here in composition API is very declarative, you know exactly when you're importing something from a specific file, what it's being named, or you can rename it if it has a conflict with that specific file. And you don't have to deal with all the assumptions that the options API can often provide for you, which again, can be helpful in certain contexts.

But in the context of reusability of code, the mixins definitely became a fun one for a lot of people that we learned.

Learn Straight from the Experts Who Shape the Modern Web

  • In-depth Courses
  • Industry Leading Experts
  • Learning Paths
  • Live Interactive Workshops
Get Unlimited Access Now