Replies: 1 comment
-
Event ChainingSynchronous event handlers cannot be stopped or pre-empted by another event, they must run to completion. However, it is possible to break up a synchronous event handler via Event Chaining, which places an event at the back of the queue and allows other events in the queue to be processed. The clock example makes use of this technique. The idea is to have the sync event handler only perform one step of the calculation, check a flag, and then recursively queue itself if the calculation should continue. Consider the following example, which uses an explicit import asyncio
import reflex as rx
class State(rx.State):
# The main countdown variable.
counter: int = 0
# _running is a backend flag that prevents a new chain from starting
# while the old one is still running.
_running: bool = False
async def decrement(self):
self.counter -= 1
await asyncio.sleep(0.3)
if self.counter > 0 and self._running:
# If the counter is still positive, queue another decrement operation.
return State.decrement
else:
# Otherwise, stop the chain and reset the flag.
self._running = False
def restart(self, value: int):
self.counter = value
if not self._running:
# Only queue a new chain if the old one is not running.
self._running = True
return State.decrement
def stop(self):
# Signal to stop recursively processing the decrement chain.
self._running = False
def index() -> rx.Component:
return rx.vstack(
rx.heading(State.counter, font_size="2em"),
rx.button("Start/Restart", on_click=lambda: State.restart(10)),
rx.button("Stop", on_click=State.stop),
spacing="1.5em",
padding_top="10%",
)
# Add state and page to the app.
app = rx.App()
app.add_page(index)
app.compile() Avoiding Multiple EventsAs for the follow up question:
The best way to accomplish this is to use a flag similar to import random
import time
import reflex as rx
def some_expensive_x_calculation() -> int:
time.sleep(2)
return random.randint(0, 100)
class State(rx.State):
doing_x: bool = False
x: int = 0
def do_x(self):
if self.doing_x:
return
self.doing_x = True
yield # send the `doing_x` flag to the frontend for rendering
try:
self.x = some_expensive_x_calculation()
finally:
self.doing_x = False
def index():
return rx.vstack(
rx.heading("x: ", State.x),
rx.cond(
State.doing_x,
rx.spinner(),
rx.button("Do x", on_click=State.do_x),
)
)
app = rx.App()
app.add_page(index)
app.compile() This technique can be used to prevent event handler from setting |
Beta Was this translation helpful? Give feedback.
-
https://discord.com/channels/1029853095527727165/1061874061250150441/1153646128542273576
Hey, I have two questions that I didnt find answer in docs.
event handlers are run synchronously. Is it possible to stop already running event and start new one? Im trying to create countdown from 10 to 0 and button that starts this countdown. Everytime I click the button I would like to countdown start again from 10 and not waiting first to go to 0 and start counting from 10 again.
Is it possible to stop adding new event_handlers before old one is not finished? I would like to prevent user to click 10 times and change state 10 times
Beta Was this translation helpful? Give feedback.
All reactions