Wrapping around transition component throws when transition is toggled before it has completed previously
Version
3.0.11
Reproduction link
Steps to reproduce
Toggle the check box to start the transition, then toggle it again before it has completed.
Try replacing the custom-transition
component with the standard transition
What is expected?
Should be able to toggle a transition as quickly as I like.
What is actually happening?
A warning and two errors are thrown. Any further attempts to toggle the transition at all produces the same errors.
[Vue warn]: Unhandled error during execution of scheduler flush. This is likely a Vue internals bug. Please open an issue at https://new-issue.vuejs.org/?repo=vuejs/vue-next
at <BaseTransition appear=false persisted=false mode=undefined ... >
at <Transition name="fade-inout" class="fade-inout" >
at <CustomTransition name="fade-inout" >
at <App>
Uncaught TypeError: Cannot read property '0' of undefined
at Object.on_console (index.969554ad.js:1)
at pw.handle_repl_message (index.969554ad.js:1)
at handle_event (index.969554ad.js:1)
Uncaught (in promise) TypeError: Cannot read property '_leaveCb' of null
at Object.beforeEnter (vue.runtime.esm-browser.js:3490)
at mountElement (vue.runtime.esm-browser.js:5938)
at processElement (vue.runtime.esm-browser.js:5882)
at patch (vue.runtime.esm-browser.js:5802)
at componentEffect (vue.runtime.esm-browser.js:6369)
at reactiveEffect (vue.runtime.esm-browser.js:342)
at callWithErrorHandling (vue.runtime.esm-browser.js:1335)
at flushJobs (vue.runtime.esm-browser.js:1561)
It has something to do with specifying classes on the transition component. If I only add the class for my transition onto the elements inside the transition, everything works okay. But if I then also set any class on the transition component, even one that doesn't exist, by binding or attribute, the same errors occur.
Custom classes are not controlled by Transition, Not just class binding any other property will result in the same error. e.g: style="color:#000"
Change the code as below, it will works fine.
<transition v-bind="$props">
<slot></slot>
</transition>
and
.fade-inout {
transition-property: opacity;
transition-duration: 1000ms;
transition-timing-function: ease-out;
}
- //
.fade-inout-enter-active, .fade-inout-leave-active {
transition-property: opacity;
transition-duration: 1000ms;
transition-timing-function: ease-out;
}
.fade-inout-enter-from, .fade-inout-leave-to {
opacity: 0;
}
Change the code as below, it will works fine
Apparently not in Chrome 89, Edge 90, or Safari 14. Using enter-active, leave-active
, if the transition switches state while it's already mid-transition, it will jump to the start of the next transition, instead of continuing from where it was when the transition state was changed.
I see that it works in Firefox though, but setting classes on the transition component and toggling the transition too quickly still throws.
In both, it works totally fine and as expected (the class binding is passed to the target of the transition) if you let the transition finish before setting it off again.
As @edison1105 said, you need to pass the class
to the element, not the transition. Maybe the problem comes from somewhere else.
it will jump to the start of the next transition, instead of continuing from where it was when the transition state was changed.
That is expected and the from classes are set when entering/leaving to set the beginning of the transition
you need to pass the
class
to the element, not the transition.
@posva Yeah, that's exactly what I was trying to avoid having to do in the first place. If I want a reusable set of enter/leave transition components and don't want them to be ugly in Chrome, Edge, and Safari if they change state too quickly, I'd have to add a class corresponding to the transition to everything I apply it to? What do I do when I want to transition a slot and don't want the user of my component to have to think about adding transition classes on their end?
That is expected and the from classes are set when entering/leaving to set the beginning of the transition
Right... until I saw that Firefox displays the transitions the way I want them to work, I assumed the way they work in Chrome was correct. That's why I was trying work around it by not using the enter/leave-active
classes and having the CSS transition applied through the lifetime of the element so you never see it jump to the from
states. Since transition
does pass the class
binding and attribute to it's target element, I assumed I could just make a wrapper around transition that adds a class with the name of the transition, and I put the CSS transition property on it instead the enter/leave-active
classes. It almost works, except that it throws if the transition is toggled while it's already in the middle of transitioning.
If I'm not supposed to do that at all, and I really don't want to require explicitly setting the transition class on the elements/components themselves in addition to wrapping them in Transitions, and I'm not going to be satisfied breaking the best quality of CSS transitions (continuity), I suppose I'll have to find some other way to get what I am looking for. If I can use the Javascript hooks, it shouldn't be too difficult, I suppose.
@A-Babin
maybe changing v-if
with v-show
will get you where you want to get.
I am not sure what breaks the logic with v-if and transition, but v-show behaves better.
Bind the class
(or other non-props binding) to the transition
component, which will perform the attribute fallthrough:
https://github.com/vuejs/vue-next/blob/293b41ba3be095452f9648b0b35dfd9022a3621e/packages/runtime-core/src/componentRenderUtils.ts#L144
This resulted in cloning the original VNode and rendering it, but the leavingVNodesCache
always stores the original VNode, not the cloned one:
After mounting, the cloned VNode has the vnode.el
property, but the original VNode does not. I cannot give a better solution for now...
Just spent 4 hours trying to debug this problem, and only then found this issue. Passing non-prop attributes to a component that has another component as its root that has Transition as its root causes a crash when enter transition is triggered while the leave transition is ongoing.
Removing the data-i-cause-a-crash
in Component.vue fixes the issue. Setting inheritAttrs: false in Dropdown.vue and adding explicit v-bind="$attrs" to the div inside the TransitionFade component also fixes it.