Unintentionally Mutating Props — Transcript

Transcript of the free Vue.js lesson Unintentionally Mutating Propswatch the video lesson.

In order to illustrate this next mistake, I've set up the following scenario. Inside of a parent component, I'm keeping up with some kind of form data here. In our case, it is the login data or the email and password for user. Now, I'm using inside of this component a custom component called login form.

On that login form, I'm using the model, passing in the login data so that any initial data in the login form here can fill out the fields in the component. And then upon submitting the form, I should be able to send the altered data back up to the parent component. This is actually a pattern I use fairly commonly in my own applications, and it usually works great. However, in this particular implementation, I have an issue.

If I type in either one of my fields here, you'll notice that the data updates immediately in the parent and doesn't wait for me to hit Log In. Hiding in this bug is the next mistake I want to discuss. view, let's take a look at what the code is doing. Here we've defined a model value prop with TypeScript and then used the defined props macro to pass the types in and register it with the component.

Then we have also defined an emit, that is the update model value event, which is required for vModel to work And then whenever we submit the form, we omit that event. Now, I haven't included a payload with the event yet. We'll do that in just a moment. But ultimately, this is when the values in the parent should change.

only when updateModelValue is omitted. And you'll notice it's not being omitted anywhere else throughout this component. So why is data in my parent component changing without my child component omitting an event? Well, the issue is that here on lines and nineteen on the email input and on the password input, I'm using the model directly on the components props.

So model value dot email and model value dot password. This is a huge no-no because you never want to modify your props directly. You simply want to omit events and let the parent handle things accordingly. You might expect, then, that this would throw an error or a warning in the console, letting you know you're doing something wrong.

But actually, it doesn't. Why not? Well, it's because we're actually modifying a property on the prop as opposed to the actual prop itself. If I were to appear in my script setup section, say const props equals the result of defined prompts, and then modify the model value prop directly, we would indeed get a console warning.

And yeah, that works because model value is target read-only. But that doesn't trigger, that doesn't fire when you're modifying properties on a prop that's an object. And this issue has definitely bit me before in real life projects. My teammates and I even dubbed the issue as Vue being quote unquote overreactive because we had such issue with it before we realized what we were doing.

So one, what do I do to avoid this issue? And two, how do we update our login form component in order to work as expected? Well, first, in order to avoid the issue, make sure you have ESLint set up in your project. I usually have it available in my IDE, but for this lesson, I temporarily disabled it.

view, and now immediately you'll see we have red squiggly lines under each of the V models here. And the reason is because there's an unexpected mutation of the model value prop. So while you don't get console warnings about mutating the property of an object prop, you will get warnings via ESLint. js developers course, if you'd like to know more.

Great. Now let's deal with the second issue, and that is actually making the login form work as expected. modelValue here that's wrong. Let's replace that with a local definition of our model value to keep up with the data locally within the component.

Now, I do need to import ref from view here. modelValue. This ensures that when the component is first created, local value gets whatever is passed in from the parent. Next, instead of using our VModel on model value then, we can use it on our local value instead.

That, of course, prevents us from modifying the prop and basically solves our issue. Just to make sure, let's go over to the browser, give the page a refresh. And now when I type in the email input, the value for email should not update in the parent. Oh, no.

But it does anyways, even though we're no longer mutating the prop. So the red squiggly lines are gone. modelValue, which is an object, to this local value. So any time we update local value, Model value also changes because they're both objects and objects are passed by reference and not by value, meaning when you update one, the other also updates.

So while we've convinced ESLint that we're no longer mutating the prompt, we actually still are. This would not be an issue if the prop was a string or if the prop was a number or a boolean. But because it is an object, we have to make sure to clone it first. So there you go.

There's your bonus mistake for this lesson. Also, another way that your view components can look, quote unquote, overreactive. The easiest way to clone an object in JavaScript is just to spread it into a new object. This won't be a complete solution if there are properties on the object that actually have object values themselves.

In other words, nested objects. But we don't. We just have two strings here. So this solution works perfectly fine.

Awesome. Over in the browser now. One more time. Hold your breath.

Perfect. Now updating the input does not update the data in the parent. But we're still not quite there. Let's make it to where it does update when we hit Log In.

This time, the data disappears altogether because we are emitting an empty payload. Let's submit a clone of the local value instead. Note that it is important to clone it for the exact same reason that we cloned model value coming down into the component. Cool.

That basically covers the mistakes part of this lesson. But in order to make the component work, a hundred percent, there is one last thing we need to do. Right now, if I update the login data in the parent component, the local data in the child component does not update as well, really breaking the expectation for vModel. In order to keep local value always in sync with the model value prompt, we can watch the model value prop and update local value accordingly.

And whoops, I forgot to add a deep to my watcher because yes, that applies not only to arrays, but objects as well. These mistakes are real y'all. And okay, there we go. To recap, never, ever, ever mutate a prop.

While it's easy to say, sometimes it's not quite as easy to actually do, especially when you're working with props that are of the type object or of the type array. Sometimes your console will help you out by giving you warnings. Sometimes ESLint will help you out by giving you warnings. But sometimes you just have to understand what is going on in the code.