Subscribe on changes!

Wrapping around transition component throws when transition is toggled before it has completed previously

avatar
May 1st 2021

Version

3.0.11

Reproduction link

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)

avatar
May 1st 2021

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.

avatar
May 2nd 2021

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;
  }
avatar
May 2nd 2021

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.

avatar
May 3rd 2021

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

avatar
May 3rd 2021

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.

avatar
May 4th 2021

@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.

avatar
May 6th 2021

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:

https://github.com/vuejs/vue-next/blob/293b41ba3be095452f9648b0b35dfd9022a3621e/packages/runtime-core/src/components/BaseTransition.ts#L407

After mounting, the cloned VNode has the vnode.el property, but the original VNode does not. I cannot give a better solution for now...