reflex-dev / reflex

🕸️ Web apps in pure Python 🐍
https://reflex.dev
Apache License 2.0
19.75k stars 1.13k forks source link

Incremental state updates resend entire state #4156

Open dodslaser opened 5 days ago

dodslaser commented 5 days ago

Describe the bug I'm trying to adapt some of the example code in the docs to fit my needs. Essentially, I want to live-stream text line by line into a code block. From what I've understood, I should be able to append to a state variable (a list) in a background handler, and use yield each time a new line is added to send the state changes incrementally to the frontend.

When I try this approach, each time state is updated, the full state is sent to the frontend, including the previously sent lines (as part of the 'delta' section in the JSON). From what I understood, only the newly added (or removed) lines should be sent (i.e. incremental updates).

In my full scale application, I would potentially be appending to a list of thousands or more lines. It would pretty quickly get out of hand if the whole state had to be sent each time. Am I missing something obvious here? Is there a better way of achieving this?

To Reproduce

import reflex as rx
import asyncio as aio

class LiveState(rx.State):
    data: list[str] = []
    _counter: int = 0
    _n_tasks: int = 0

    @rx.background
    async def update_data(self):
        async with self:
            if self._n_tasks > 0:
                return
            self._n_tasks += 1

        while True:
            async with self:
                self.data.append(f"Message #{self._counter}")
                self._counter += 1
                yield
            await aio.sleep(1)

@rx.page(on_load=LiveState.update_data)
def index():
    return rx.code_block(LiveState.data.join("\n"))

Expected behavior Only the changes in state are sent, not the whole thing.

Specifics (please complete the following information):

masenf commented 4 days ago

we have a backlog item to implement https://datatracker.ietf.org/doc/html/rfc6902, but the behavior you are currently seeing is expected. reflex does not send the full state, only the vars that have changed... but if the var that has changed is already very large, then yes, it does send the complete value over.

dodslaser commented 6 hours ago

Ah, ok, that makes sense. JSON Patch would definitely be neat. In the meantime I'll probably solve this with WebSockets and custom components.