Subscribe on changes!

Computed properties become frozen in complicated circumstances

avatar
Apr 1st 2021

Version

3.0.10

Reproduction link

https://codesandbox.io/s/vue-3-reactivity-bug-ufpmi

Steps to reproduce

  1. Input something in the first and second fields. See, that 'Form Invalid' changes to 'Form Valid'.
  2. Clear the input and click on the 'Toggle Modal' button.
  3. Input something in the first and second fields. See, that 'Form Invalid' doesn't change to 'Form Valid'.

What is expected?

It's expected, that after remounting the component the label 'Form Invalid' changes to 'Form Valid' in the last step.

What is actually happening?

After remounting the component the label 'Form Invalid' doesn't change to 'Form Valid' in the last step.

avatar
Apr 2nd 2021

Just in case, if someone encounter a similar problem. I have found at least a temporary solution.

The reason of this behavior is clearly explained in the links above. The computed properties are binding to the current instance. You can see it in the source code:

export function computed<T>(
  getterOrOptions: ComputedGetter<T> | WritableComputedOptions<T>
) {
  const c = _computed(getterOrOptions as any)
  recordInstanceBoundEffect(c.effect)
  return c
}

Notice that _computed is the function from @vue/reactivity that doesn't perform this binding, so it's possible to fix the example in this issue by importing computed and other reactivity functions from this package, i.e. replace:

import { computed } from 'vue'

By:

import { computed } from '@vue/reactivity'

The fixed example to illustrate the idea.

If you try to implement this approach in your own project, remember about the usage note:

This package is inlined into Global & Browser ESM builds of user-facing renderers (e.g. @vue/runtime-dom), but also published as a package that can be used standalone. The standalone build should not be used alongside a pre-bundled build of a user-facing renderer, as they will have different internal storage for reactivity connections. A user-facing renderer should re-export all APIs from this package.