|
8 | 8 | from reactpy.core._life_cycle_hook import LifeCycleHook |
9 | 9 | from reactpy.core.hooks import strictly_equal, use_effect |
10 | 10 | from reactpy.core.layout import Layout |
11 | | -from reactpy.testing import DisplayFixture, HookCatcher, assert_reactpy_did_log, poll |
| 11 | +from reactpy.testing import ( |
| 12 | + DEFAULT_TYPE_DELAY, |
| 13 | + DisplayFixture, |
| 14 | + HookCatcher, |
| 15 | + assert_reactpy_did_log, |
| 16 | + poll, |
| 17 | +) |
12 | 18 | from reactpy.testing.logs import assert_reactpy_did_not_log |
13 | 19 | from reactpy.utils import Ref |
14 | | -from tests.tooling.common import DEFAULT_TYPE_DELAY, update_message |
| 20 | +from tests.tooling.common import update_message |
15 | 21 |
|
16 | 22 |
|
17 | 23 | async def test_must_be_rendering_in_layout_to_use_hooks(): |
@@ -600,6 +606,46 @@ async def effect(): |
600 | 606 | event_that_never_occurs.set() |
601 | 607 |
|
602 | 608 |
|
| 609 | +async def test_use_async_effect_shield(): |
| 610 | + component_hook = HookCatcher() |
| 611 | + effect_ran = asyncio.Event() |
| 612 | + effect_finished = asyncio.Event() |
| 613 | + stop_waiting = asyncio.Event() |
| 614 | + |
| 615 | + @reactpy.component |
| 616 | + @component_hook.capture |
| 617 | + def ComponentWithShieldedEffect(): |
| 618 | + @reactpy.hooks.use_async_effect(dependencies=None, shield=True) |
| 619 | + async def effect(): |
| 620 | + effect_ran.set() |
| 621 | + await stop_waiting.wait() |
| 622 | + effect_finished.set() |
| 623 | + |
| 624 | + return reactpy.html.div() |
| 625 | + |
| 626 | + async with Layout(ComponentWithShieldedEffect()) as layout: |
| 627 | + await layout.render() |
| 628 | + |
| 629 | + await effect_ran.wait() |
| 630 | + |
| 631 | + # Trigger re-render which would normally cancel the effect |
| 632 | + component_hook.latest.schedule_render() |
| 633 | + |
| 634 | + # Give the loop a chance to process the render logic and potentially cancel |
| 635 | + await asyncio.sleep(0.1) |
| 636 | + |
| 637 | + # Verify effect hasn't finished yet but also wasn't cancelled |
| 638 | + assert not effect_finished.is_set() |
| 639 | + |
| 640 | + # Now allow the effect to finish |
| 641 | + stop_waiting.set() |
| 642 | + |
| 643 | + # The re-render should complete now that the shielded effect is done |
| 644 | + await layout.render() |
| 645 | + |
| 646 | + await asyncio.wait_for(effect_finished.wait(), 1) |
| 647 | + |
| 648 | + |
603 | 649 | async def test_async_effect_sleep_is_cancelled_on_re_render(): |
604 | 650 | """Test that async effects waiting on asyncio.sleep are properly cancelled.""" |
605 | 651 | component_hook = HookCatcher() |
|
0 commit comments