@@ -88,9 +88,10 @@ class LifeCycleHook:
88
88
"__weakref__" ,
89
89
"_context_providers" ,
90
90
"_current_state_index" ,
91
- "_effect_generators " ,
91
+ "_pending_effects " ,
92
92
"_render_access" ,
93
93
"_rendered_atleast_once" ,
94
+ "_running_effects" ,
94
95
"_schedule_render_callback" ,
95
96
"_schedule_render_later" ,
96
97
"_state" ,
@@ -109,7 +110,8 @@ def __init__(
109
110
self ._rendered_atleast_once = False
110
111
self ._current_state_index = 0
111
112
self ._state : tuple [Any , ...] = ()
112
- self ._effect_generators : list [AsyncGenerator [None , None ]] = []
113
+ self ._pending_effects : list [AsyncGenerator [None , None ]] = []
114
+ self ._running_effects : list [AsyncGenerator [None , None ]] = []
113
115
self ._render_access = Semaphore (1 ) # ensure only one render at a time
114
116
115
117
def schedule_render (self ) -> None :
@@ -131,7 +133,7 @@ def use_state(self, function: Callable[[], T]) -> T:
131
133
132
134
def add_effect (self , effect_func : Callable [[], AsyncGenerator [None , None ]]) -> None :
133
135
"""Add an effect to this hook"""
134
- self ._effect_generators .append (effect_func ())
136
+ self ._pending_effects .append (effect_func ())
135
137
136
138
def set_context_provider (self , provider : ContextProviderType [Any ]) -> None :
137
139
self ._context_providers [provider .type ] = provider
@@ -150,29 +152,32 @@ async def affect_component_will_render(self, component: ComponentType) -> None:
150
152
async def affect_component_did_render (self ) -> None :
151
153
"""The component completed a render"""
152
154
self .unset_current ()
153
- del self .component
154
155
self ._rendered_atleast_once = True
155
156
self ._current_state_index = 0
156
157
self ._render_access .release ()
157
158
158
159
async def affect_layout_did_render (self ) -> None :
159
160
"""The layout completed a render"""
160
161
try :
161
- await gather (* [g .asend (None ) for g in self ._effect_generators ])
162
+ await gather (* [g .asend (None ) for g in self ._pending_effects ])
163
+ self ._running_effects .extend (self ._pending_effects )
162
164
except Exception :
163
- logger .exception ("Error during effect execution" )
165
+ logger .exception ("Error during effect startup" )
166
+ finally :
167
+ self ._pending_effects .clear ()
164
168
if self ._schedule_render_later :
165
169
self ._schedule_render ()
166
170
self ._schedule_render_later = False
171
+ del self .component
167
172
168
173
async def affect_component_will_unmount (self ) -> None :
169
174
"""The component is about to be removed from the layout"""
170
175
try :
171
- await gather (* [g .aclose () for g in self ._effect_generators ])
176
+ await gather (* [g .aclose () for g in self ._running_effects ])
172
177
except Exception :
173
- logger .exception ("Error during effect cancellation " )
178
+ logger .exception ("Error during effect cleanup " )
174
179
finally :
175
- self ._effect_generators .clear ()
180
+ self ._running_effects .clear ()
176
181
177
182
def set_current (self ) -> None :
178
183
"""Set this hook as the active hook in this thread
@@ -192,7 +197,7 @@ def unset_current(self) -> None:
192
197
raise RuntimeError ("Hook stack is in an invalid state" ) # nocov
193
198
194
199
def _is_rendering (self ) -> bool :
195
- return self ._render_access .value ! = 0
200
+ return self ._render_access .value = = 0
196
201
197
202
def _schedule_render (self ) -> None :
198
203
try :
0 commit comments