Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Updates to deep/nested reactive props does not trigger a DOM update #1387

Closed
ConradSollitt opened this issue Jun 17, 2020 · 4 comments
Closed

Comments

@ConradSollitt
Copy link

Version

3.0.0-beta.15

Reproduction link

https://codepen.io/conrad-sollitt/pen/yLeaoGB

Steps to reproduce

The CodePen contains 4 main options depending on how code is commented:

  • Vue 2
  • Vue 3 - Composition API with Vue.reactive
  • Vue 3 - Composition API with Vue.ref
  • Vue 3 - Options API

The HTML allows Vue 2/Vue 3 to be toggled by commenting/un-commenting the correct version.

Based on whether Vue 2 or Vue 3 is used the JS will run code the correct version. When using Vue 3 two constants at the top of the JS file can be changed to affect what API is used:

const useCompositionAPI = true
const useReactive = false

Once mounted/onMounted is called a window.setTimeout() runs after 500 milliseconds and makes updates on the nested list.

What is expected?

When using Vue 2 (both Prod and Dev) the updates always show in the DOM.

What is actually happening?

When using Vue 3 (regardless of the API used) the updates will only show if one of the top-level properties is set and not one of the deep/nested properties.

Code comments in the window.setTimeout() describe how to make it work with Vue 3.


This may be related to issue: #262

If it is then this feature will not be supported in Vue 3. If it can't be supported I think it would be a good idea to document deep/nested object updates somewhere in the final documentation.

I'm opening the issue because based on the DOM it's not clear to me if it should update or not. Additionally when using Dev Tools you can see that all nested objects use a Proxy object so this could be a Browser/JavaScript issue as well.

From the API Docs:

https://composition-api.vuejs.org/api.html#reactive

The reactive conversion is "deep": it affects all nested properties. In the ES2015 Proxy based implementation, the returned proxy is not equal to the original object.

Last Friday I opened the following issue related to the global window object: #1353

I'm happy to follow up my report that I feel that the new Vue 3 API is easy to work with for migration and keeping Vue 2 Options API while supporting the new Composition API works great and is well thought out! I've been waiting a while to migrate as I wanted to wait till near release rather than making many changes while the new API was in early development.

I was able to migrate 7 mini web apps to Vue 3 while keeping support for Vue 2 (actually running Vue 2 in production still until the final release) but each web app has the option for both Vue 2 or Vue 3 simply by swapping out the CDN link. This was the main issue that I found but it only required a few lines of code extra in one app in order to work.

@shawnwildermuth
Copy link

I'm seeing something similar. But it's an update behind. When the nested object changes, the UI doesn't change until a second render cycle is called.

@LinusBorg
Copy link
Member

LinusBorg commented Jun 21, 2020

The issue is that you create a plain, non-reactive object here:

const obj = {
  name: 'test (2 child list items should show)',
  items: [],
}

and then continue to mutate this plain, nonreactive object:

obj.items.push({ label:'item-1', score:1 })

In Vue 3, this change will not be observed, even though before that mutation, you added the object to an reactive array.

  • In Vue 2, adding the object to the reactive array makes the object instance itself reactive, as Vue 2 will manipulate the original object's getters and setters.
  • In Vue 3, this will not do anything to the original object, as reactivity is observed through proxies only. The original object will stay non-reactive, so any change you apply to this object directly will not be registered.

So yes, this is a caveat of the new, proxy-based reactivity.

The solution is: never work with plain objects that are meant to become reactive. Always make them reactive proactively:

const obj = reactive({
  name: 'test (2 child list items should show)',
  items: [],
})

now, obj no longer references the original object, it references the reactive proxy. Any change you apply to it will be picked up.


This challenge will be covered in the documentation and, where possible, also supported by eslint rules in eslint-plugin-vue.

@shawnwildermuth
Copy link

The issue I found was in my code. Once I tried to reproduce it in Beta.15, I couldn't. So I am happy to have it closed.

@ConradSollitt
Copy link
Author

Thanks for the detailed feedback @LinusBorg

@github-actions github-actions bot locked and limited conversation to collaborators Nov 11, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants