core: reactivity does not work on root component props when using createApp
Version
3.2.20
Reproduction link
Steps to reproduce
Pass a reactive({}) object into createApp’s second parameter, update a property on it.
What is expected?
The root component should update.
What is actually happening?
The root component is not updating.
About this issue
- Original URL
- State: open
- Created 3 years ago
- Reactions: 2
- Comments: 15 (7 by maintainers)
Commits related to this issue
- Fully migrate to Vue 3 The vue and vuex versions are pinned to what is currently provided by MediaWiki core. This patch needs some non-trivial changes to the jasmine tests: In order to be able to mu... — committed to wikimedia/mediawiki-extensions-WikibaseLexeme by lucaswerkmeister 2 years ago
- Update git submodules * Update WikibaseLexeme from branch 'master' to 2a16c9e8f4a8e83c379bb88828ed7a18387bd980 - Fully migrate to Vue 3 The vue and vuex versions are pinned to what is cu... — committed to wikimedia/mediawiki-extensions by lucaswerkmeister 2 years ago
Just for the record what you can quite easily do for now is use a wrapper component like this:
When you now mutate
props,Appwill update.Is that what you refer to here?
I’m not sure I get the “not even as top level …” part that seems to make it so cumbersome. It’s literally a one-line change.
And again, I’m with you that this would be a nice addition, just documenting what does work now.
So the question is, how does one import any component, mount it as root, and update its props over time? I can’t find it in the docs.
This seems like a basic necessity and makes me wonder how it was overlooked. No one ever needs to do this?
I can’t remember when I last needed that. Though I can remember that I did use a wrapper component for such a thing once.
Guys! I figured it out! the full solution to maintain full props reactivity and get the
defineExpose()interface! Here is how:By supplying
exposevariable into the render function, and then callingchildApp.mount(el), the expose variable gets assigned fromnullto the context, from where you can accessexpose.component.exposedparam to get the exposed interface of your component!😃
Now you can use
reactive({ props })as props and they will all be reactive.I seem to need it often.
The main use case is that, when interfacing with other technologies, one often has to mount multiple root components at multiple interface points.
For example, working with data analytics dashboards like Grafana, one has to mount a root component in each dashboard panel as a Grafana plugin.
Or for example, NASA’s OpenMCT mission control dashboard allows plugins to add panels with custom visuals. The plugin system passes a plugin a DOM element, and the plugin can then append any DOM that should appear in that area. The plugin author may choose to create the DOM subtree any way they want (plain JS, Vue, React, etc).
Or for example Shopify plugins: same concept: custom DOM trees inserted into online stores to add functionality.
Another example is tech migration: a migrating an app from React (or anything else) to Vue would require data passing at the intersection points where the non-Vue tree turns into a Vue tree.
As another example, without this feature in Vue 3, importing and using 3rd party components as root components (f.e. importing a Vue chart library to stick it in a panel of some dashboard framework) becomes impossible in Vue 3 without having to make a wrapper component just to pass data through (and not even as top level properties to the wrapper component, but as nested second-level reactive properties to the wrapper component as described above), which is more cumbersome than simply setting JS properties on the 3rd-party components.
Etc.
If we’re talking about a pure Vue app that is made with nothing but Vue, then this issue disappears. Of course, in this case, the top level component usually has no props.
I’d rate this as an enhancement. To me, the props passed in always were initial values only.
app.mount()isn’t written in a way suitable to listen for a reactive effect from these props either, so it likely is this way by design.