2
2
3
3
import logging
4
4
from asyncio import gather
5
- from collections .abc import AsyncGenerator
5
+ from collections .abc import Awaitable
6
6
from typing import Any , Callable , TypeVar
7
7
8
8
from anyio import Semaphore
@@ -41,7 +41,7 @@ class LifeCycleHook:
41
41
.. testcode::
42
42
43
43
from reactpy.core._life_cycle_hook import LifeCycleHook
44
- from reactpy.core.hooks import current_hook, COMPONENT_DID_RENDER_EFFECT
44
+ from reactpy.core.hooks import current_hook
45
45
46
46
# this function will come from a layout implementation
47
47
schedule_render = lambda: ...
@@ -63,7 +63,11 @@ class LifeCycleHook:
63
63
64
64
# and save state or add effects
65
65
current_hook().use_state(lambda: ...)
66
- current_hook().add_effect(COMPONENT_DID_RENDER_EFFECT, lambda: ...)
66
+
67
+ async def effect():
68
+ yield
69
+
70
+ current_hook().add_effect(effect)
67
71
finally:
68
72
await hook.affect_component_did_render()
69
73
@@ -88,10 +92,10 @@ class LifeCycleHook:
88
92
"__weakref__" ,
89
93
"_context_providers" ,
90
94
"_current_state_index" ,
91
- "_pending_effects" ,
95
+ "_effect_cleanups" ,
96
+ "_effect_startups" ,
92
97
"_render_access" ,
93
98
"_rendered_atleast_once" ,
94
- "_running_effects" ,
95
99
"_schedule_render_callback" ,
96
100
"_schedule_render_later" ,
97
101
"_state" ,
@@ -110,8 +114,8 @@ def __init__(
110
114
self ._rendered_atleast_once = False
111
115
self ._current_state_index = 0
112
116
self ._state : tuple [Any , ...] = ()
113
- self ._pending_effects : list [AsyncGenerator [ None , None ]] = []
114
- self ._running_effects : list [AsyncGenerator [ None , None ]] = []
117
+ self ._effect_startups : list [Callable [[], Awaitable [ None ] ]] = []
118
+ self ._effect_cleanups : list [Callable [[], Awaitable [ None ] ]] = []
115
119
self ._render_access = Semaphore (1 ) # ensure only one render at a time
116
120
117
121
def schedule_render (self ) -> None :
@@ -131,9 +135,14 @@ def use_state(self, function: Callable[[], T]) -> T:
131
135
self ._current_state_index += 1
132
136
return result
133
137
134
- def add_effect (self , effect_func : Callable [[], AsyncGenerator [None , None ]]) -> None :
138
+ def add_effect (
139
+ self ,
140
+ start_effect : Callable [[], Awaitable [None ]],
141
+ clean_effect : Callable [[], Awaitable [None ]],
142
+ ) -> None :
135
143
"""Add an effect to this hook"""
136
- self ._pending_effects .append (effect_func ())
144
+ self ._effect_startups .append (start_effect )
145
+ self ._effect_cleanups .append (clean_effect )
137
146
138
147
def set_context_provider (self , provider : ContextProviderType [Any ]) -> None :
139
148
self ._context_providers [provider .type ] = provider
@@ -155,29 +164,28 @@ async def affect_component_did_render(self) -> None:
155
164
self ._rendered_atleast_once = True
156
165
self ._current_state_index = 0
157
166
self ._render_access .release ()
167
+ del self .component
158
168
159
169
async def affect_layout_did_render (self ) -> None :
160
170
"""The layout completed a render"""
161
171
try :
162
- await gather (* [g .asend (None ) for g in self ._pending_effects ])
163
- self ._running_effects .extend (self ._pending_effects )
172
+ await gather (* [start () for start in self ._effect_startups ])
164
173
except Exception :
165
174
logger .exception ("Error during effect startup" )
166
175
finally :
167
- self ._pending_effects .clear ()
168
- if self ._schedule_render_later :
169
- self ._schedule_render ()
170
- self ._schedule_render_later = False
171
- del self .component
176
+ self ._effect_startups .clear ()
177
+ if self ._schedule_render_later :
178
+ self ._schedule_render ()
179
+ self ._schedule_render_later = False
172
180
173
181
async def affect_component_will_unmount (self ) -> None :
174
182
"""The component is about to be removed from the layout"""
175
183
try :
176
- await gather (* [g . aclose () for g in self ._running_effects ])
184
+ await gather (* [clean () for clean in self ._effect_cleanups ])
177
185
except Exception :
178
186
logger .exception ("Error during effect cleanup" )
179
187
finally :
180
- self ._running_effects .clear ()
188
+ self ._effect_cleanups .clear ()
181
189
182
190
def set_current (self ) -> None :
183
191
"""Set this hook as the active hook in this thread
0 commit comments