1 minute read

Environment

  • Vue 2.7
  • Using the <script setup> syntax

The Problem

I built an ordinary select box that receives the selected value through v-model. I wanted to implement business logic where selecting a certain option triggers a validation function, which displays a message and prevents that option from being selected.

But a problem arose. The validation function worked fine, and it blocked the change to v-model… yet on screen, the option I had just selected was still showing as selected. Since v-model wasn’t changed, shouldn’t the option value remain the one it was before the change?

When I looked inside the Vue instance, the v-model value had been changed exactly as I intended. But the select box bound to v-model was displaying the wrong value.

The Cause

  • One-line summary: because v-model didn’t change, no rerender happened.
  1. A select tag’s value is a value provided by the HTML DOM API.
  2. When you set an input’s value (here, the selected value) via Vue’s v-model, that is a value inside the Vue instance.
  3. Vue rerenders when a value inside the Vue instance changes.
  4. The HTML DOM API’s input value had already changed the moment the user selected it.
  5. But since the value inside the Vue instance didn’t change, no rerender happened.
  6. The input value set by Vue’s v-model and the input value from the DOM API ended up out of sync.

The Solution

There are several approaches, but changing the key is the cleanest way.

<template>
  <MyComponent :key="componentKey" />
</template>
<script setup>
import { ref } from 'vue';
const componentKey = ref(0);

const forceRerender = () => {
  componentKey.value += 1;
};
</script>

Don’t waste your time attempting weird hacks like $forceUpdate.

References

Tags:

Categories:

Updated:

Leave a comment