@@ -89,7 +89,9 @@ Use Effect
8989
9090.. code-block ::
9191
92- use_effect(did_render)
92+ @use_effect
93+ def did_render():
94+ ... # imperative or state mutating logic
9395
9496 The ``use_effect `` hook accepts a function which may be imperative, or mutate state. The
9597function will be called immediately after the layout has fully updated.
@@ -117,12 +119,11 @@ then closing a connection:
117119
118120.. code-block ::
119121
122+ @use_effect
120123 def establish_connection():
121124 connection = open_connection()
122125 return lambda: close_connection(connection)
123126
124- use_effect(establish_connection)
125-
126127 The clean-up function will be run before the component is unmounted or, before the next
127128effect is triggered when the component re-renders. You can
128129:ref: `conditionally fire events <Conditional Effects >` to avoid triggering them each
@@ -141,14 +142,19 @@ example, imagine that we had an effect that connected to a ``url`` state variabl
141142
142143 url, set_url = use_state("https://example.com")
143144
145+ @use_effect
144146 def establish_connection():
145147 connection = open_connection(url)
146148 return lambda: close_connection(connection)
147149
148- use_effect(establish_connection)
149-
150150 Here, a new connection will be established whenever a new ``url `` is set.
151151
152+ .. warning ::
153+
154+ A component will be unable to render until all its outstanding effects have been
155+ cleaned up. As such, it's best to keep cleanup logic as simple as possible and/or
156+ to impose a time limit.
157+
152158
153159Async Effects
154160.............
@@ -160,48 +166,52 @@ simply write an async function.
160166
161167.. code-block ::
162168
163- async def non_blocking_effect():
169+ @use_effect
170+ async def my_async_effect():
164171 await do_something()
165172
166- use_effect(non_blocking_effect)
167-
168- However, if you need to do any cleanup, then you must ``yield False `` inside a try-finally
169- block and place your cleanup logic in the finally block. Yielding ``False `` indicates to
170- ReactPy that the effect will not yield again before it is cleaned up.
173+ However, if you need to do any cleanup, then you'll need to write an async generator
174+ instead. The generator should run the effect logic in a ``try `` block, ``yield `` control
175+ back to ReactPy, and then run the cleanup logic in a ``finally `` block:
171176
172177.. code-block ::
173178
174- async def blocking_effect():
175- await do_something()
179+ @use_effect
180+ async def my_async_effect():
176181 try:
177- yield False
182+ await effect_logic()
183+ yield
178184 finally:
179- await do_cleanup()
180-
181- use_effect(blocking_effect)
185+ await cleanup_logic()
182186
183- If you have a long-lived effect, you may ``yield True `` multiple times. ``True `` indicates
184- to ReactPy that the effect will yield again if the effect doesn't need to be cleanup up
185- yet.
187+ When a component is re-rendered or unmounted the effect will be cancelled if it is still
188+ running. This will typically happen for long-lived effects. One example might be an
189+ effect that opens a connection and then responds to messages for the lifetime of the
190+ connection:
186191
187192.. code-block ::
188193
189- async def establish_connection():
190- connection = await open_connection()
194+ @use_effect
195+ async def my_async_effect():
196+ conn = await open_connection()
191197 try:
192198 while True:
193- yield False
194- await connection.send(create_message())
195- handle_message(await connection.recv())
199+ msg = await conn.recv()
200+ await handle_message(msg)
196201 finally:
197- await close_connection(connection)
202+ await close_connection(conn)
203+
204+ .. warning ::
205+
206+ Because an effect can be cancelled at any time, it's possible that the cleanup logic
207+ will run before all of the effect logic has finished. For example, in the code
208+ above, we exclude ``conn = await open_connection() `` from the ``try `` block because
209+ if the effect is cancelled before the connection is opened, then we don't need to
210+ close it.
198211
199- use_effect(non_blocking_effect)
212+ .. note ::
200213
201- Note that, if an effect needs to be cleaned up, it will only do so when the effect
202- function yields control back to ReactPy. So you should ensure that either, you can
203- be sure that the effect will yield in a timely manner, or that you enforce a timeout
204- on the effect. Otherwise, ReactPy may hang while waiting for the effect to yield.
214+ We don't need a yield statement here because the effect only ends when it's cancelled.
205215
206216
207217Manual Effect Conditions
0 commit comments