ソースを参照

Fix x-show updating DOM inconsistently if state changes too fast (#2968)

* Fixed hide and show functions being called in different stages of the event loop, causing inconsistent DOM updates on rapid state changes

* Added test for correct show/hide order

* Fixed test case
mgschoen 3 年 前
コミット
5c6ec4d172

+ 3 - 2
packages/alpinejs/src/directives/x-transition.js

@@ -131,7 +131,8 @@ window.Element.prototype._x_toggleAndCascadeWithTransitions = function (el, valu
     // it keeps running in background. setTimeout has a lower priority in the
     // event loop so it would skip nested transitions but when the tab is
     // hidden, it's not relevant.
-    let clickAwayCompatibleShow = () => {document.visibilityState === 'visible' ? requestAnimationFrame(show) : setTimeout(show)}
+    const nextTick = document.visibilityState === 'visible' ? requestAnimationFrame : setTimeout;
+    let clickAwayCompatibleShow = () => nextTick(show);
 
     if (value) {
         if (el._x_transition && (el._x_transition.enter || el._x_transition.leave)) {
@@ -167,7 +168,7 @@ window.Element.prototype._x_toggleAndCascadeWithTransitions = function (el, valu
 
             closest._x_hideChildren.push(el)
         } else {
-            queueMicrotask(() => {
+            nextTick(() => {
                 let hideAfterChildren = el => {
                     let carry = Promise.all([
                         el._x_hidePromise,

+ 16 - 0
tests/cypress/integration/directives/x-show.spec.js

@@ -143,3 +143,19 @@ test('x-show takes precedence over style bindings for display property',
         get('span:nth-of-type(2)').should(haveAttribute('style', 'color: red; display: none;'))
     }
 )
+
+test('x-show executes consecutive state changes in correct order',
+    html`
+        <div
+            x-data="{ isEnabled: false }"
+            x-init="$watch('isEnabled', () => { if (isEnabled) isEnabled = false })"
+        >
+            <button id="enable" x-show="!isEnabled" @click="isEnabled = true"></button>
+            <button id="disable" x-show="isEnabled" @click="isEnabled = false"></button>
+        </div>
+    `,
+    ({ get }) => {
+        get('button#enable').should(beVisible())
+        get('button#disable').should(beHidden())
+    }
+)