Home > Software engineering >  Why does vue3 not re-render a ref of reactive variable when it's not deeply nested
Why does vue3 not re-render a ref of reactive variable when it's not deeply nested

Time:01-31

I am trying to understand the Composition-API Reactivity in vue3. I want to build a search bar, which shall instantly show me the results in the template.

For that, I have a reactive variable search and a reactive variable results.

If I declare results as ref() which should automatically convert to reactive() due because it's an Array [EXAMPLE A] or declare directly this array as reactive() [EXAMPLE B] the results are not rendered directly in the template. I write one letter, nothing happens, but when I write the second letter, I see the result of the search before with the single letter.

When I deeply nest the results [EXAMPLE C], then it works, I see instantly all results.

Why is that? I don't understand why A or B does not work.

<script setup>
let search = ref("");

// EXAMPLE A
// This does not work, the search results are shown one "tick" later
const results = ref([]);

// EXAMPLE B
// This does not work, the search results are shown one "tick" later
const results = reactive([]);

// EXAMPLE C
// This does work, when the result array is deeply-nested in a reactive object
const query = reactive({ results: [] });

watch(search, (value) => {
    axios
        .post("/search", { search: value })
        .then(response => {
            // EXAMPLE A / B
            results = response.data;
            
            // EXAMPLE C
            query.results = response.data;
        })
        .catch();
});
</script>

<template>
    <!-- EXAMPLE A / B -->
    <div v-for="product in results" :key="product.id">
        <div>{{ product.id }}</div>
        <div>{{ product.name }}</div>
    </div>
    
    <!-- EXAMPLE C -->
    <div v-for="product in query.results" :key="product.id">
        <div>{{ product.id }}</div>
        <div>{{ product.name }}</div>
    </div>
</template>

CodePudding user response:

I am assuming that the sample code that you posted is a contrived example, since it gives runtime errors. In JS, you can't reassign to const, which you are doing several times within your sample code. Therefore, I can't reproduce the behavior that you claim to see.

To fix Example A and Example B, you need to change how you assign the response to the reactive object.

  • Example A - you are using a ref, where you need to assign to the value property of the ref - results.value = response.data. See the Vue docs on ref for more.
  • Example B - you can't reassign to a reactive - this is a limitation of using reactive. You can work around this either by using Object.assign (see https://stackoverflow.com/a/65733741/6767625) or by using Example C, which as you noted works fine.
  • Related