How to fix "Avoid mutating a prop directly" in Vue
Tags: vue
Published: 2019-09-12 07:54:04

---
title: How to fix "Avoid mutating a prop directly" in Vue
published: true
description: Have you ever faced this problem in vue - 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.
tags: vue,vuejs
cover_image: https://thepracticaldev.s3.amazonaws.com/i/si266n05wz3sh3oqo5hz.png
---

![Featured Image](https://i.ibb.co/4Rd4d9L/featured.png)

Have you ever face this error -

> 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.

![Error Message](https://i.ibb.co/Q8xFfQ9/error.png)

The reason why Vue doesn’t recommend to modify the `prop` data because if the parent component has a different value, the data in the child component will be re-render.

## How to solve it?

I’m gonna create a simple Vue component consist of input with `v-model` attached to it and another Card component that accepts the `v-model` data as a `props` and displays it.

This is how the interface will looks alike.
![Interface](https://i.ibb.co/yqDYC6q/interface.png)

_App.vue_

```vue
<template>
  <div id="app" class="max-w-sm mx-auto py-16">
    <div class="flex flex-col mb-6">
      <div class="mb-2">
        <label for="inline-full-name">Enter your number</label>
      </div>
      <div class="w-full">
        <input
          v-model="number"
          id="inline-full-name"
          class="input"
          type="number"
        />
      </div>
    </div>
    <Card :number="number" />
  </div>
</template>

<script>
import Card from "@/components/Card";

export default {
  name: "app",
  components: {
    Card
  },
  data() {
    return {
      number: 20
    };
  }
};
</script>

<style lang="postcss">
#app {
  font-family: "Avenir", Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}

label {
  @apply blocktext-gray-500font-boldmb-1pr-4text-left;
}
.input {
  @apply bg-gray-200appearance-noneborder-2border-gray-200roundedw-fullpy-2px-4text-gray-700leading-tight;
}
.input:focus {
  @apply outline-nonebg-whiteborder-purple-500;
}
</style>
```

_Card.vue_

```vue
<template>
  <div
    class="bg-red-100 flex justify-between items-center border border-red-400 text-red-700 px-4 py-3 rounded relative"
    role="alert"
  >
    <strong class="font-bold">Your number is {{ number }}</strong>
    <svg
      class="fill-current h-6 w-6 text-red-500"
      @click.prevent="addNumber"
      xmlns="http://www.w3.org/2000/svg"
      viewBox="002424"
      width="24"
      height="24"
    >
      <path
        class="heroicon-ui"
        d="M1711a1100102h-4v4a11001-20v-4H7a110010-2h4V7a1100120v4h4z"
      />
    </svg>
  </div>
</template>

<script>
export default {
  props: {
    number: {
      required: true
    }
  },
  methods: {
    addNumber() {
      this.number++;
    }
  }
};
</script>
```

This is how the interface works.

![Part-1](https://i.ibb.co/SK9vrjD/part-1.gif)

Every-time user type in the input, it will reflect directly in the card below. It’s because we're passing `v-model: number` as `prop` in the `Card` component.

Any value that user’s typing will reflect immediately.

My next question is what’s going to happen if we’re going to manipulate the data in the child component, which is the `Card` component.

I’ll add one button in the `Card` component that triggers a `method`that increase the `value`.

_Card.vue_

```vue
<script>
export default {
  props: {
    number: {
      required: true
    }
  },
  methods: {
    addNumber() {
      this.number++;
    }
  }
};
</script>
```

Surprisingly, this is what’s happening.

![Part-2](https://i.ibb.co/KwqP80s/part-2.gif)

When we want to increase the value in the child component, Vue gives us an error message that this is not recommended way.

### Why is it not recommend?

If the data _v-model_ in the parent is changed, the data in the child component will re-render.

For example, I add any number in the input, and I increase the `number` using the plus button. What happens if I add a new value in the `input`?

The value that we increased just now is getting a new value from the input.

Get it?

![Part-3](https://i.ibb.co/48ZkH2v/part-3.gif)

### How do we solve it?

Since we cannot modify the props data directly, so we need to replicate the `props` data into a variable.

In the `addNumber` methods, assign the props into a new variable, and do the calculation.

```js
let myNumber = this.number;
myNumber++;
```

The next question is, how do we send the new number value to the parent? 🤔

Thank god, there is a custom event to solve it. We need to add the custom event in the `addNumber` methods.

```js
this.$emit("update-number", myNumber);
```

It’s mean that we’re sending a `myNumber` data through `update-number` event name. Since we’re sending a custom data, the parent component needs to listen to the `update-number` event.

In the `App.vue`, add the listener to the `Card` component.

```js
<Card:number="number" @update-number="update"/>
```

If there’s an `emit` event called `update-number` triggered in the child component, `update` function will be called.

In the `App.vue` file, add the `update` function in the `methods` section.

```js
methods:{
    update(number){
        this.number=number;
    }
}
```

Remember, when we’re sending the custom event, we’re sending `myNumber` as a parameter. So, in the `update` function need to have one parameter to get the data.

```js
this.$emit("update-number", myNumber);
```

After that, the new value we get from the custom event will be assigned to the `number` data. Since, the child component data will be re-render if every time the `props` data changed, we can see the instant update in the `Card` component.

![Part-4](https://i.ibb.co/8jmVSXW/part-4.gif)

## Source Code

App.vue

```vue
<template>
  <div id="app" class="max-w-sm mx-auto py-16">
    <div class="flex flex-col mb-6">
      <div class="mb-2">
        <label for="inline-full-name">Enter your number</label>
      </div>
      <div class="w-full">
        <input
          v-model="number"
          id="inline-full-name"
          class="input"
          type="number"
        />
      </div>
    </div>
    <Card :number="number" @update-number="update" />
  </div>
</template>

<script>
import Card from "@/components/Card";
export default {
  name: "app",
  components: {
    Card
  },
  data() {
    return {
      number: 20
    };
  },
  methods: {
    update(number) {
      this.number = number;
    }
  }
};
</script>

<style lang="postcss">
#app {
  font-family: "Avenir", Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}

label {
  @apply block text-gray-500 font-bold mb-1 pr-4 text-left;
}
.input {
  @apply bg-gray-200 appearance-none border-2 border-gray-200 rounded w-full py-2 px-4 text-gray-700 leading-tight;
}
.input:focus {
  @apply outline-none bg-white border-purple-500;
}
</style>
```

Card.vue

```vue
<template>
  <div
    class="bg-red-100 flex justify-between items-center border border-red-400 text-red-700 px-4 py-3 rounded relative"
    role="alert"
  >
    <strong class="font-bold">Your number is {{ number }}</strong>
    <svg
      class="fill-current h-6 w-6 text-red-500 cursor-pointer"
      @click.prevent="addNumber"
      xmlns="http://www.w3.org/2000/svg"
      viewBox="0 0 24 24"
      width="24"
      height="24"
    >
      <path
        class="heroicon-ui"
        d="M17 11a1 1 0 0 1 0 2h-4v4a1 1 0 0 1-2 0v-4H7a1 1 0 0 1 0-2h4V7a1 1 0 0 1 2 0v4h4z"
      />
    </svg>
  </div>
</template>

<script>
export default {
  props: {
    number: {
      required: true
    }
  },
  methods: {
    addNumber() {
      let myNumber = this.number;
      myNumber++;
      this.$emit("update-number", myNumber);
    }
  }
};
</script>
```

Originally published at [https://jakzaizzat.com](https://jakzaizzat.com/avoid-mutating-a-prop-directly)