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

TypeError: dynamic css on dynamically imported component #9264

Closed
Disservin opened this issue Sep 21, 2023 · 9 comments · Fixed by #9370
Closed

TypeError: dynamic css on dynamically imported component #9264

Disservin opened this issue Sep 21, 2023 · 9 comments · Fixed by #9370
Labels
🐞 bug Something isn't working 🔩 p2-edge-case

Comments

@Disservin
Copy link
Contributor

Disservin commented Sep 21, 2023

Vue version

3.3.4

Link to minimal reproduction

https://github.com/Disservin/vue-dynamic-import-css
Vue Playground Link

Steps to reproduce

npm install
npm run dev

What is expected?

I'd expect no TypeError's and that the component is properly mounted and has my dynamic class.

What is actually happening?

Open your browser console and you should be greeted by a TypeError.

TypeError: Failed to execute 'observe' on 'MutationObserver': parameter 1 is not of type 'Node'.

System Info

No response

Any additional comments?

If you comment out the following part in the Foo Component, you get no error.

.hover-link {
    padding: v-bind("bindings.padding") px;
}

or if you remove the class from the component
:class="loading ? 'classA' : 'classB'"

    <component
        :is="getComponent()"
        :id="component.id"
        :settings="component.settings"
        :class="loading ? 'classA' : 'classB'"
    />
@Disservin
Copy link
Contributor Author

Disservin commented Sep 21, 2023

It seems to me that for some reason async components are missing the parentNode and thus the error?

@Disservin
Copy link
Contributor Author

When looking at the created instances, there are actually two, I guess that makes sense ?

{uid: 6, vnode: {…}, type: {…}, parent: {…}, appContext: {…}, …}
{uid: 4, vnode: {…}, type: {…}, parent: {…}, appContext: {…}, …}

From what I can tell they are for classB and classA, respectively, but uid: 4 has no subTree.el.parentNode while uid: 6 has it?

@posva
Copy link
Member

posva commented Sep 27, 2023

I think someone reported the same bug in Pinia but with a boiled down repro so I added it here simplified

@Disservin
Copy link
Contributor Author

Ah nice, seems to be related, thanks for the smaller reproduction! I initially encountered this also with my pinia store, but then I figured out that it isnt related to pinia.
Weirdly enough you can get rid of the error in my reproduction if you do these changes

-    <component
-        :is="getComponent()"
-        :id="component.id"
-        :settings="component.settings"
-        :class="loading ? 'classA' : 'classB'"
-    />
+    <MyComp
+        :id="component.id"
+        :settings="component.settings"
+        :class="loading ? 'classA' : 'classB'"
+    />

-const getComponent = () => {
-    return defineAsyncComponent(() => Promise.resolve(VFoo));
-};

+const MyComp = defineAsyncComponent(() => Promise.resolve(VFoo));

@noahgregory-basis
Copy link

Do we have any updates on this? I reported a bug (#9617) a while ago that was then mentioned as a possible duplicate of this.

@yyx990803
Copy link
Member

Note that with

const getComponent = () => {
    return defineAsyncComponent(() => Promise.resolve(VFoo));
};

and

<component :is="getComponent()">

You are defining a new component on every update. To the parent component, it is rendering a different child component every time. So every time the parent component updates, Foo will be unmounted and re-mounted. You should definitely avoid that.

That said the error is still something that is relevant.

@yyx990803 yyx990803 added 🔩 p2-edge-case and removed 🔨 p3-minor-bug Priority 3: this fixes a bug, but is an edge case that only affects very specific usage. labels Jun 7, 2024
yyx990803 pushed a commit that referenced this issue Jun 7, 2024
@Disservin
Copy link
Contributor Author

Disservin commented Jun 7, 2024

Note that with

const getComponent = () => {
    return defineAsyncComponent(() => Promise.resolve(VFoo));
};

and

<component :is="getComponent()">

You are defining a new component on every update. To the parent component, it is rendering a different child component every time. So every time the parent component updates, Foo will be unmounted and re-mounted. You should definitely avoid that.

That said the error is still something that is relevant.

Ah thanks for pointing that out! I anyway now have something which looks like this (which i hope doesnt have this problem lol)

const UiComponent = (() => {
    return defineAsyncComponent(
        () => import(`./VFoo.vue`),
    );
})();
<ui-component>
</ui-component>

@yyx990803
Copy link
Member

Why not just

const UiComponent = defineAsyncComponent(() => import(`./VFoo.vue`))

What's the point of wrapping it in another function?

@Disservin
Copy link
Contributor Author

Disservin commented Jun 9, 2024

I had some additional logic in the first arrow function to build the import path, which I removed when posting here, but yea that is functionally the same anyway

@github-actions github-actions bot locked and limited conversation to collaborators Jun 24, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
🐞 bug Something isn't working 🔩 p2-edge-case
Projects
None yet
4 participants