Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clicking any stateful component while using streamable object crashes nextjs #1797

Closed
sullyo opened this issue Jun 3, 2024 · 1 comment
Closed
Assignees
Labels
ai/rsc bug Something isn't working

Comments

@sullyo
Copy link

sullyo commented Jun 3, 2024

Description

When using the streamable api, If you submit an action to generate a component, and then try to click anything stateful the whole app will crash.

This only happens for the first ~0.25 seconds when there is no ui generated from the streamable

To reproduce:

Create a streamable similar to the code example below, and then click a button that uses a react hook. The app will crash entirely

Code example

Some arbitrary streamable method

export async function generateRecipe() {
  const recipeStream = createStreamableValue<PartialLasagna>();

  recipeAgent()
    .then(async (partialObjectStream) => {
      for await (const partialObject of partialObjectStream.partialObjectStream) {
        if (partialObject.recipe) {
          recipeStream.update(partialObject);
        }
      }
    })
    .finally(() => {
      recipeStream.done();
    });

  return recipeStream.value;
}

export type UIState = Array<{
  display: ReactNode;
}>;

export const AI = createAI({
  initialUIState: [] as UIState,
  actions: {
    generateRecipe,
  },
});

Reading data here:

"use client";

import { PartialLasagna } from "@/app/types";
import { useStreamableValue } from "ai/rsc";
import React from "react";

interface DisplayRecipeProps {
  recipe: PartialLasagna;
}
export default function DisplayRecipe({ recipe }: DisplayRecipeProps) {
  const [data, pending, error] = useStreamableValue<PartialLasagna>(recipe);
  console.log(data);

  return (
    <div className="flex flex-col gap-2">
      <p>{data?.recipe?.name}</p>
      <p>{JSON.stringify(data, null, 2)}</p>
    </div>
  );
}

used here:

"use client";
import { generateRecipe } from "@/app/action";
import { useState } from "react";
import DisplayRecipe from "./display-recipe";

export default function Recipe() {
  const [recipe, setRecipe] = useState<{
    display: JSX.Element;
  } | null>(null);
  const [test, setTest] = useState("")

  async function handleGenerateRecipe() {
    setRecipe({
      display: <DisplayRecipe recipe={await generateRecipe()} />,
    });
  }

  return (
    <div className="flex flex-col items-center">
      <button onClick={setTest(true)}>Click me to break</button>
      <button onClick={handleGenerateRecipe}>Generate recipe</button>

      {recipe && <div>{recipe?.display}</div>}
    </div>
  );
}

Additional context

No response

@lgrammel lgrammel added bug Something isn't working ai/rsc labels Jun 3, 2024
@jeremyphilemon
Copy link
Contributor

@sullyo Thanks for reporting!

The setTest(true) below seems like a red flag, given that it is executed on mount and can cause an infinite loop.

<button onClick={setTest(true)}>Click me to break</button>

Correction

<button
  onClick={() => {
    setTest(true);
  }}
>
  Click me to break
</button>

Can you confirm if you're still seeing the stream break after this fix?

@jeremyphilemon jeremyphilemon self-assigned this Jun 4, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
ai/rsc bug Something isn't working
Projects
None yet
Development

No branches or pull requests

3 participants