Subscribe on changes!

Enable non-reactive mutable data to be patched on re-rendering

avatar
Jun 29th 2023

What problem does this feature solve?

Currently, when re-rendering occurs, changes to object literals defined with const are not patched even though the object is mutable. This means that if you define object literals with const and later you update the properties of these object literals, the changes will not be reflected during re-rendering.

For example, consider the following code:

<script>
import { ref } from 'vue'
const isActive = ref(true)
const hasError = ref(false)
</script>
<template>
  <div
    :class="{ active: isActive, 'text-danger': hasError }"
  ></div>
</template>

And later you want to rewrite the class attribute values in your script like this:

<script>
import { ref } from 'vue'
const isActive = ref(true)
const hasError = ref(false)
const classObject ={ active: isActive, 'text-danger': hasError }
</script>
<template>
  <div :class="classObject"></div>
</template>

In the above code, the changes made to classObject will not be reflected when re-rendering occurs.

To make it work correctly, you would need to use the ref() or reactive(), replace const with let or use an intermediary variable like this:

<script>
import { ref } from 'vue'
const isActive = ref(true)
const hasError = ref(false)

const classObject =ref({ active: isActive, 'text-danger': hasError })
// or
let classObject ={ active: isActive, 'text-danger': hasError }
// or
const foo = { active: isActive, 'text-danger': hasError }
const classObject = foo

</script>
<template>
  <div :class="classObject"></div>
</template>

However, these workarounds can be cumbersome and inconvenient. It would be more convenient to have a system where mutable data is patched even if it is defined as const.

What does the proposed API look like?

<script>
import { ref } from 'vue'
const isActive = ref(true)
const hasError = ref(false)
const classObject ={ active: isActive, 'text-danger': hasError }
</script>
<template>
  <div :class="classObject"></div>
</template>

With this proposed API, the changes made to classObject would be properly patched during re-rendering.

avatar
Jun 29th 2023

This likely won't be happening/be possible.

What you are describing in the status quo is simply how Vue's Reactivity at its very core works: a component re-renders when a reactive dependency that is being used in the template is changed. if you use a nonreactive object in the template - for class attributes, child component props, or simply in {{ }} interpolation, changes of that object are not reactive and will therefore not trigger a re-render.

Wrapping your const in reactive() is not a workaround, it's the normal way to handle this type of situation.

Re-Rendering in reaction to changes of non-reactive objects won't really be feasable. Reactivity relies on proxies (or as in Vue 2 - getters and setters), plain object changes can't be detected.

avatar
Jun 29th 2023

In this case, I believe re-rendering will occur. This is because Vue.js's reactive system, particularly ReactiveEffect, watches reactive data accessed during rendering. In addition, ":class" accesses properties of the specified object in the attribute value, and since that property is a ref object, it triggers re-rendering. However, the processing of the "patch" in the rendering mechanism is skipped because the bindingType is set to BindingTypes.SETUP_CONST. I am considering whether it would be better to change this setting.

avatar
Jun 29th 2023

Ah. Thanks for the follow-up, I now see I misunderstood your description. Seems indeed like an adge case worth looking into.