-
-
Notifications
You must be signed in to change notification settings - Fork 8.4k
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
reactivity does not work on root component props when using createApp #4874
Comments
Here is a non-ideal workaround that requires modifying the props shape of the root component (this may not be ideal if the root component is imported from a third party). |
I would say you need to pass an object of refs to props or an object of reactive values but that seems to warn 🤔 |
I'd rate this as an enhancement. To me, the props passed in always were initial values only.
|
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. |
And don't get me wrong, it would make sense to allow/support it I think. It's just not a bug in my book. |
It seems like a "regression" in terms of what we could do in Vue 2 but no longer can in Vue 3. This is Vue 2: import ThirdPartyComponent from 'somewhere'
const component = new Vue({...ThirdPartyComponent})
// ...
component.someProp = 123 // it works. In Vue 3? |
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. |
Just for the record what you can quite easily do for now is use a wrapper component like this: import { h, reactive } from 'vue'
import App from './App.vue'
const props = reactive({ whatever })
const app = createApp({
render: () => h(App, props)
})
app.mount('#app') When you now mutate 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. |
@LinusBorg That's good to know. I never used How exactly does |
Sorry for confusing you - I simply forgot to wrap it in |
Ah, thanks, I will try this out when I circle back to it. |
You can make it even shorter when using a functional component: |
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 mutate the inEditMode prop, we need to wrap the component in a wrapper, because of vuejs/core#4874 not being fixed even a year later. That in turn prevents us from calling the methods defined on the component under test. Thus we need two functions to create new widgets depending on which part of the interface we want to test, methods or props. Also, this no longer asserts the internal inEditMode value, because 1) asserting internals is bad practice anyway, and 2) we no longer have access to that in the wrapped component. Bug: T304534 Change-Id: I82313a5eb6e8f19088de4a2e831666cdb656b1eb Co-Authored-By: Michael Große <[email protected]>
* Update WikibaseLexeme from branch 'master' to 2a16c9e8f4a8e83c379bb88828ed7a18387bd980 - 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 mutate the inEditMode prop, we need to wrap the component in a wrapper, because of vuejs/core#4874 not being fixed even a year later. That in turn prevents us from calling the methods defined on the component under test. Thus we need two functions to create new widgets depending on which part of the interface we want to test, methods or props. Also, this no longer asserts the internal inEditMode value, because 1) asserting internals is bad practice anyway, and 2) we no longer have access to that in the wrapped component. Bug: T304534 Change-Id: I82313a5eb6e8f19088de4a2e831666cdb656b1eb Co-Authored-By: Michael Große <[email protected]>
This is nice and seemed to have worked for me for reactivity, but I am using the result of But when using the render function way such as Any idea how to get the defineExpose result? |
Guys! I figured it out! the full solution to maintain full props reactivity and get the function createComponent ({ app, component, props, el }) {
let expose = null
const childApp = createApp({ render: () => expose = h(component, props) })
Object.assign(childApp._context, app._context)
childApp.mount(el)
return expose.component.exposed
} By supplying :-) Now you can use |
@LinusBorg This would be quite useful for custom renderers. In these cases we can create an application in each navigation, modals or any other flow in which you need to inject a component into a new native view. The proposed solutions do not work with Of course this is not something that is used on the front end, but to make the custom renderer more flexible it should support this. Additionally, a component can receive props like these: const refItem = ref([{foo: "bar1"}, {foo: "bar2"}]);
const reactiveItem = reactive({foo: "bar"});
const nonReactiveProp = "foo"
createApp(component, {refItem, reactiveItem, nonReactiveProp}) This is something totally valid in vue components, why not in the main component? |
Thanks mate. You saved my life anyway !!!! |
this should definitely be added to Vue docs, being able to update manually mounted root components props is a core feature of Vue for anyone who renders their apps via different rendering engines (and thats basically anyone who has any kind of legacy, and is unable to serve the prebuilt It shouldnt require an hours worth of googling to find the correct keywords to stumble across this github issue to finally figure out how to achieve this thing, that took 5 minutes to do in Vue2. |
Version
3.2.20
Reproduction link
sfc.vuejs.org/
Steps to reproduce
Pass a
reactive({})
object intocreateApp
'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.
The text was updated successfully, but these errors were encountered: