FastUI icon indicating copy to clipboard operation
FastUI copied to clipboard

Infinite loop when using ServerLoad without components parameter.

Open hasansezertasan opened this issue 1 year ago • 2 comments

I mentioned this at #123.

Example code:

from __future__ import annotations as _annotations

import random
import time

from fastapi import FastAPI
from fastapi.responses import HTMLResponse
from fastui import AnyComponent, FastUI, prebuilt_html
from fastui import components as c
from fastui.events import GoToEvent, PageEvent


def Master(*components: AnyComponent, title: str | None = None) -> list[AnyComponent]:
    return [
        c.PageTitle(text="Lotto Numbers Generator"),
        c.Navbar(
            title="Still Lotto Numbers Generator",
            title_event=GoToEvent(url="/"),
        ),
        c.Page(
            components=[
                *((c.Heading(text=title),) if title else ()),
                *components,
            ],
        ),
    ]


app = FastAPI()


@app.get("/api/", response_model=FastUI, response_model_exclude_none=True)
def components_view() -> list[AnyComponent]:
    return Master(
        c.Div(
            components=[
                c.Heading(text="Lotto Numbers Generator", level=2),
                c.Paragraph(text="Generate lotto numbers for your next game."),
                c.Button(text="Generate a lotto number!", on_click=PageEvent(name="add-number")),
                c.Div(
                    components=[
                        c.ServerLoad(
                            path="/add-number",
                            load_trigger=PageEvent(name="add-number"),
                        ),
                    ],
                    class_name="py-2",
                ),
            ],
            class_name="border-top mt-3 pt-1",
        ),
        title="Quite simple and Understandable Example",
    )


@app.get("/api/add-number", response_model=FastUI, response_model_exclude_none=True)
async def modal_view() -> list[AnyComponent]:
    time.sleep(0.5)
    return [
        c.Paragraph(
            text=f"Your lotto number is {random.randint(1, 99)}!",
        ),
        c.ServerLoad(
            path="/add-number",
            load_trigger=PageEvent(name="add-number"),
        ),
    ]


@app.get("/{path:path}")
async def html_landing() -> HTMLResponse:
    """Simple HTML page which serves the React app, comes last as it matches all paths."""
    return HTMLResponse(prebuilt_html(title="FastUI Demo"))

If you navigate to /, you'll see the ServerLoad event keeps triggering even fi you don't click the button.

hasansezertasan avatar Dec 29 '23 20:12 hasansezertasan

It seems the problem is here:

https://github.com/pydantic/FastUI/blob/cec25c61a7cc5a716d05d21039f95be3e8dac0e8/src/npm-fastui/src/components/ServerLoad.tsx#L15C14-L23

Maybe the condition should depend on loadTrigger and not on components:

export const ServerLoadComp: FC<ServerLoad> = ({ path, components, loadTrigger, sse }) => {
  if (loadTrigger) {
    return <ServerLoadDefer path={path} components={components ?? []} loadTrigger={loadTrigger} sse={sse} />
  } else if (sse) {
    return <ServerLoadSSE path={path} />
  } else {
    return <ServerLoadFetch path={path} />
  }
}

meirdev avatar Dec 30 '23 20:12 meirdev

I'll look soon.

samuelcolvin avatar Dec 30 '23 20:12 samuelcolvin