Weekly Vue News — #72 — Avoid Mutating a Prop Directly
Hi 👋
Unfortunately, my newsletter service (Revue by Twitter) shuts down in January 2023 … in about four weeks….
Of course, I will continue publishing weekly Vue news!
I will migrate all existing subscribers to a custom solution: Store the subscriber list on Supabase, manage the content on my Nuxt 3-powered newsletter website and send the emails using Amazon SES.
Next week’s issue will look quite different but will contain the same high-quality content. If you encounter any problems, send me a email.
Have a great week ☀️
Vue Tip: Avoid Mutating a Prop Directly
If you are using Vue 3 + ESLint and try to mutate a prop in your Vue component, you should see the following error:
Unexpected mutation of “todo” prop. eslintvue/no-mutating-props
If you are using Vue 2, Vue will throw the following error/warning in your browser’s console:
[Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop’s value.
Explanation
First, we look at the official documentation to understand why Vue throws that error.
Why do we see the error
When objects and arrays are passed as props, while the child component cannot mutate the prop binding, it will be able to mutate the object or array’s nested properties. This is because JavaScript objects and arrays are passed by reference, and it is unreasonably expensive for Vue to prevent such mutations.
Why is it a problem
The main drawback of such mutations is that it allows the child component to affect the parent state in a way that isn’t obvious to the parent component, potentially making it more difficult to reason about the data flow in the future. As a best practice, you should avoid such mutations unless the parent and child are tightly coupled by design.
Recommended solution
In most cases, the child should emit an event to let the parent perform the mutation.
Demo
Let’s look at a code example. I’m using a Todo app to demonstrate the error. The source code is interactively available on StackBlitz.
Let’s start by taking a look at the TodoList.vue
component:
TodoList.vue
contains a reactive variable todos
that includes an array of Todo items. In the template, each item is rendered via TodoItem.vue
component:
Assuming ESLint is correctly configured, you should see the following ESLint error if you open TodoItem.vue
in your editor:
Mutating props is an anti-pattern
We want to write components that are easy to maintain.
In a maintainable component, only the component itself should be able to change its own state.
Additionally, only the component’s parent should be able to change the props.
These two rules are essential to ensure Vue’s One-Way Data Flow to make our app’s data flow easier to understand.
⚠️ If we mutate the props, we violate both rules and break Vue’s data flow!
In addition, every time the parent component is updated, all props in the child component will be refreshed with the latest value.
Solution
In most cases, the error can be solved using a computed property.
In our example, instead of mutating the prop, we emit an event to the parent. The parent is then responsible for updating the Todo list correctly.
Let’s use a writeable computed property in TodoItem.vue
. Its getter access the prop's value, and the setter emits an event to the parent:
Finally, we need to react to the emitted event in TodoList.vue
and update the todos
accordingly:
The prop is not mutated with this solution, and we correctly use the one-way data flow in our Vue application.
The amazing Michael Thiessen also wrote a detailed article about this topic.
Curated Vue Content
📕 Announcing Vite 4
👉🏻 Vite is now using Rollup 3, which allowed them to simplify Vite’s internal asset handling and has many improvements
🛠️ Maska 2.1
👉🏻 A zero-dependency input mask
👉🏻 Happy in vanilla situations, but can also integrate with Vue 2/3.
🛠️ Harlem 3.0
👉🏻 A simple extensible state management for Vue 3.
👉🏻 Provides a simple functional API for creating, reading, and mutating state.
Quote of the Week
JavaScript Tip: Sum an array with reduce()
The reduce()
method executes a user-supplied "reducer" callback function on each element of the array, in order, passing in the return value from the calculation on the preceding element. The final result of running the reducer across all elements of the array is a single value.
The first time that the callback is run there is no “return value of the previous calculation”. If supplied, an initial value may be used in its place. Otherwise the array element at index 0 is used as the initial value and iteration starts from the next element (index 1 instead of index 0).
Perhaps the easiest-to-understand case for reduce()
is to return the sum of all the elements in an array:
The reducer walks through the array element-by-element, at each step adding the current array value to the result from the previous step (this result is the running sum of all the previous steps) — until there are no more elements to add.
Curated Web Development Content
📅 JSWorld Conference 2023
👉🏻 The largest JS conference in the world, and it’s back in Amsterdam on Feb. 8–10.
👉🏻 30 talks on Vite, Vue and Svelte by the Creator of Vite and Vue, Evan You and Core Team members.
📕 Tidy up your ES6 imports
Antoinse shows three ways how to tidy up ES6 imports:
👉🏻 Barrel pattern
👉🏻 Aliases
👉🏻 Order imports
📕 Testing static types in TypeScript
👉🏻 When it comes to TypeScript code there are far fewer options for testing its compile-type types.
📕 Speeding up the JavaScript ecosystem — one library at a time
👉🏻 Most popular libraries can be sped up by avoiding unnecessary type conversions or by avoiding creating functions inside functions.
📕 TypeScript 5.0 Iteration Plan
👉🏻 Eagerly awaiting TypeScript 5?
👉🏻 The first beta is due in late January 2023.
📕 Why would anyone need JavaScript generator functions?
👉🏻 James explains that generator functions in JS may not be essential but they do have utility and can change how you approach certain problems.
📕 Templating in HTML
👉🏻 Kitty explains why the template element in HTML is pretty handy.
📕 Tips for analyzing logs
14 useful tips including the following:
👉🏻 Search for the request’s ID
👉🏻 Build a timeline
🛠️ Modern Errors
👉🏻 A JS/TS library for handling errors
👉🏻 It features simple patterns for creating error classes, setting error properties, wrapping or aggregating errors, and separating known and unknown errors.
🛠️ Snow.js
👉🏻 Add a snow effect to a web page
👉🏻 It’s that time… (in Germany at least)