55import reactpy
66from reactpy import html
77from reactpy .config import REACTPY_DEBUG_MODE
8- from reactpy .core .hooks import (
9- COMPONENT_DID_RENDER_EFFECT ,
10- LifeCycleHook ,
11- current_hook ,
12- strictly_equal ,
13- )
8+ from reactpy .core .hooks import LifeCycleHook , strictly_equal
149from reactpy .core .layout import Layout
1510from reactpy .testing import DisplayFixture , HookCatcher , assert_reactpy_did_log , poll
1611from reactpy .testing .logs import assert_reactpy_did_not_log
1712from reactpy .utils import Ref
1813from tests .tooling .common import DEFAULT_TYPE_DELAY , update_message
14+ from tests .tooling .concurrency import WaitForEvent
1915
2016
2117async def test_must_be_rendering_in_layout_to_use_hooks ():
@@ -327,15 +323,15 @@ def CheckNoEffectYet():
327323async def test_use_effect_cleanup_occurs_before_next_effect ():
328324 component_hook = HookCatcher ()
329325 cleanup_triggered = reactpy .Ref (False )
330- cleanup_triggered_before_next_effect = reactpy . Ref ( False )
326+ cleanup_triggered_before_next_effect = WaitForEvent ( )
331327
332328 @reactpy .component
333329 @component_hook .capture
334330 def ComponentWithEffect ():
335331 @reactpy .hooks .use_effect (dependencies = None )
336332 def effect ():
337333 if cleanup_triggered .current :
338- cleanup_triggered_before_next_effect .current = True
334+ cleanup_triggered_before_next_effect .set ()
339335
340336 def cleanup ():
341337 cleanup_triggered .current = True
@@ -353,7 +349,7 @@ def cleanup():
353349 await layout .render ()
354350
355351 assert cleanup_triggered .current
356- assert cleanup_triggered_before_next_effect .current
352+ await cleanup_triggered_before_next_effect .wait ()
357353
358354
359355async def test_use_effect_cleanup_occurs_on_will_unmount ():
@@ -395,10 +391,11 @@ def cleanup():
395391 assert cleanup_triggered_before_next_render .current
396392
397393
398- async def test_memoized_effect_on_recreated_if_dependencies_change ():
394+ async def test_memoized_effect_is_recreated_if_dependencies_change ():
399395 component_hook = HookCatcher ()
400396 set_state_callback = reactpy .Ref (None )
401- effect_run_count = reactpy .Ref (0 )
397+ effect_ran = WaitForEvent ()
398+ run_count = 0
402399
403400 first_value = 1
404401 second_value = 2
@@ -410,29 +407,31 @@ def ComponentWithMemoizedEffect():
410407
411408 @reactpy .hooks .use_effect (dependencies = [state ])
412409 def effect ():
413- effect_run_count .current += 1
410+ nonlocal run_count
411+ effect_ran .set ()
412+ run_count += 1
414413
415414 return reactpy .html .div ()
416415
417416 async with reactpy .Layout (ComponentWithMemoizedEffect ()) as layout :
418417 await layout .render ()
419418
420- assert effect_run_count .current == 1
419+ await effect_ran .wait ()
420+ effect_ran .clear ()
421421
422422 component_hook .latest .schedule_render ()
423423 await layout .render ()
424424
425- assert effect_run_count .current == 1
426-
427425 set_state_callback .current (second_value )
428426 await layout .render ()
429427
430- assert effect_run_count .current == 2
428+ await effect_ran .wait ()
429+ effect_ran .clear ()
431430
432431 component_hook .latest .schedule_render ()
433432 await layout .render ()
434433
435- assert effect_run_count . current == 2
434+ assert run_count == 2
436435
437436
438437async def test_memoized_effect_cleanup_only_triggered_before_new_effect ():
@@ -474,7 +473,7 @@ def cleanup():
474473
475474
476475async def test_use_async_effect ():
477- effect_ran = asyncio . Event ()
476+ effect_ran = WaitForEvent ()
478477
479478 @reactpy .component
480479 def ComponentWithAsyncEffect ():
@@ -486,13 +485,13 @@ async def effect():
486485
487486 async with reactpy .Layout (ComponentWithAsyncEffect ()) as layout :
488487 await layout .render ()
489- await asyncio . wait_for ( effect_ran .wait (), 1 )
488+ await effect_ran .wait ()
490489
491490
492491async def test_use_async_effect_cleanup ():
493492 component_hook = HookCatcher ()
494- effect_ran = asyncio . Event ()
495- cleanup_ran = asyncio . Event ()
493+ effect_ran = WaitForEvent ()
494+ cleanup_ran = WaitForEvent ()
496495
497496 @reactpy .component
498497 @component_hook .capture
@@ -516,10 +515,10 @@ async def effect():
516515
517516async def test_use_async_effect_cancel (caplog ):
518517 component_hook = HookCatcher ()
519- effect_ran = asyncio . Event ()
520- effect_was_cancelled = asyncio . Event ()
518+ effect_ran = WaitForEvent ()
519+ effect_was_cancelled = WaitForEvent ()
521520
522- event_that_never_occurs = asyncio . Event ()
521+ event_that_never_occurs = WaitForEvent ()
523522
524523 @reactpy .component
525524 @component_hook .capture
@@ -562,7 +561,7 @@ def bad_effect():
562561
563562 return reactpy .html .div ()
564563
565- with assert_reactpy_did_log (match_message = r"Layout post-render effect .* failed " ):
564+ with assert_reactpy_did_log (match_message = r"Error while applying effect " ):
566565 async with reactpy .Layout (ComponentWithEffect ()) as layout :
567566 await layout .render () # no error
568567
@@ -588,7 +587,7 @@ def bad_cleanup():
588587 return reactpy .html .div ()
589588
590589 with assert_reactpy_did_log (
591- match_message = r"Pre-unmount effect .*? failed " ,
590+ match_message = r"Error while cleaning up effect " ,
592591 error_type = ValueError ,
593592 ):
594593 async with reactpy .Layout (OuterComponent ()) as layout :
@@ -845,7 +844,7 @@ def bad_callback():
845844
846845async def test_use_effect_automatically_infers_closure_values ():
847846 set_count = reactpy .Ref ()
848- did_effect = asyncio . Event ()
847+ did_effect = WaitForEvent ()
849848
850849 @reactpy .component
851850 def CounterWithEffect ():
@@ -873,7 +872,7 @@ def some_effect_that_uses_count():
873872
874873async def test_use_memo_automatically_infers_closure_values ():
875874 set_count = reactpy .Ref ()
876- did_memo = asyncio . Event ()
875+ did_memo = WaitForEvent ()
877876
878877 @reactpy .component
879878 def CounterWithEffect ():
@@ -1001,13 +1000,16 @@ async def test_error_in_layout_effect_cleanup_is_gracefully_handled():
10011000 def ComponentWithEffect ():
10021001 @reactpy .hooks .use_effect (dependencies = None ) # always run
10031002 def bad_effect ():
1004- msg = "The error message"
1005- raise ValueError (msg )
1003+ def bad_cleanup ():
1004+ msg = "The error message"
1005+ raise ValueError (msg )
1006+
1007+ return bad_cleanup
10061008
10071009 return reactpy .html .div ()
10081010
10091011 with assert_reactpy_did_log (
1010- match_message = r"post-render effect .*? failed " ,
1012+ match_message = r"Error while cleaning up effect " ,
10111013 error_type = ValueError ,
10121014 match_error = "The error message" ,
10131015 ):
@@ -1211,12 +1213,12 @@ def incr_effect_count():
12111213
12121214 async with reactpy .Layout (SomeComponent ()) as layout :
12131215 await layout .render ()
1214- assert effect_count .current == 1
1216+ await poll ( lambda : effect_count .current ). until_equals ( 1 )
12151217 value .current = "string" # new string instance but same value
12161218 hook .latest .schedule_render ()
12171219 await layout .render ()
12181220 # effect does not trigger
1219- assert effect_count .current == 1
1221+ await poll ( lambda : effect_count .current ). until_equals ( 1 )
12201222
12211223
12221224async def test_use_state_named_tuple ():
@@ -1232,28 +1234,3 @@ def some_component():
12321234 state .current .set_value (2 )
12331235 await layout .render ()
12341236 assert state .current .value == 2
1235-
1236-
1237- async def test_error_in_component_effect_cleanup_is_gracefully_handled ():
1238- component_hook = HookCatcher ()
1239-
1240- @reactpy .component
1241- @component_hook .capture
1242- def ComponentWithEffect ():
1243- hook = current_hook ()
1244-
1245- def bad_effect ():
1246- raise ValueError ("The error message" )
1247-
1248- hook .add_effect (COMPONENT_DID_RENDER_EFFECT , bad_effect )
1249- return reactpy .html .div ()
1250-
1251- with assert_reactpy_did_log (
1252- match_message = "Component post-render effect .*? failed" ,
1253- error_type = ValueError ,
1254- match_error = "The error message" ,
1255- ):
1256- async with reactpy .Layout (ComponentWithEffect ()) as layout :
1257- await layout .render ()
1258- component_hook .latest .schedule_render ()
1259- await layout .render () # no error
0 commit comments