From 669cf9776c81a85ae0f031391ad248827d4a8d67 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Fri, 12 Apr 2024 19:15:45 +0900 Subject: [PATCH] refactor(react-server): use official `createServerReference` (#283) --- .../basic/src/routes/test/action/_action.tsx | 10 ++++++ .../basic/src/routes/test/action/_client.tsx | 35 +++++++++++++++++++ .../basic/src/routes/test/action/page.tsx | 4 +++ .../react-server/examples/basic/tsconfig.json | 2 +- packages/react-server/src/entry/browser.tsx | 5 ++- packages/react-server/src/entry/server.tsx | 2 ++ .../src/features/server-action/client.tsx | 1 + 7 files changed, 57 insertions(+), 2 deletions(-) diff --git a/packages/react-server/examples/basic/src/routes/test/action/_action.tsx b/packages/react-server/examples/basic/src/routes/test/action/_action.tsx index 991bf35fc..4d6b91a6a 100644 --- a/packages/react-server/examples/basic/src/routes/test/action/_action.tsx +++ b/packages/react-server/examples/basic/src/routes/test/action/_action.tsx @@ -35,3 +35,13 @@ export async function actionCheckAnswer(formData: FormData) { const message = answer === 2 ? "Correct!" : "Wrong!"; return { message }; } + +export async function actionStateTest(prevArg: unknown, formData: FormData) { + const result = { prev: prevArg, form: [...formData.entries()] }; + console.log("[actionStateTest]", result); + return result; +} + +export async function actionBindTest(boundArg: string, formData: FormData) { + console.log("[actionBindTest]", { boundArg, form: [...formData.entries()] }); +} diff --git a/packages/react-server/examples/basic/src/routes/test/action/_client.tsx b/packages/react-server/examples/basic/src/routes/test/action/_client.tsx index e7df3d33f..3859cdd6e 100644 --- a/packages/react-server/examples/basic/src/routes/test/action/_client.tsx +++ b/packages/react-server/examples/basic/src/routes/test/action/_client.tsx @@ -4,7 +4,9 @@ import { useActionData } from "@hiogawa/react-server/client"; import React from "react"; import ReactDom from "react-dom"; import { + actionBindTest, actionCheckAnswer, + actionStateTest, addMessage, changeCounter, type getMessages, @@ -117,6 +119,39 @@ export function ActionDataTest() { ); } +// TODO +export function UseActionStateTest() { + const [data, formAction, isPending] = ReactDom.useFormState( + actionStateTest, + null, + ); + + React.useEffect(() => { + console.log("[useActionState]", data, isPending); + }, [data, isPending]); + + return ( +
+ + +
+ ); +} + +export function ClientActionBindTest() { + const formAction = actionBindTest.bind(null, "bound!!"); + return ( +
+ + +
+ ); +} + // https://react.dev/reference/react-dom/hooks/useFormStatus export function FormStateTest() { return ( diff --git a/packages/react-server/examples/basic/src/routes/test/action/page.tsx b/packages/react-server/examples/basic/src/routes/test/action/page.tsx index 5736be91c..edd362a5e 100644 --- a/packages/react-server/examples/basic/src/routes/test/action/page.tsx +++ b/packages/react-server/examples/basic/src/routes/test/action/page.tsx @@ -2,9 +2,11 @@ import { changeCounter, getCounter, getMessages } from "./_action"; import { ActionDataTest, Chat, + ClientActionBindTest, Counter, Counter2, FormStateTest, + UseActionStateTest, } from "./_client"; export default async function Page() { @@ -17,6 +19,8 @@ export default async function Page() { + + ); diff --git a/packages/react-server/examples/basic/tsconfig.json b/packages/react-server/examples/basic/tsconfig.json index 7d581715e..e58bfa260 100644 --- a/packages/react-server/examples/basic/tsconfig.json +++ b/packages/react-server/examples/basic/tsconfig.json @@ -16,7 +16,7 @@ "moduleResolution": "Bundler", "module": "ESNext", "target": "ESNext", - "lib": ["ESNext", "DOM"], + "lib": ["ESNext", "DOM", "DOM.Iterable"], "types": ["vite/client", "react/experimental", "react-dom/experimental"], "jsx": "react-jsx" } diff --git a/packages/react-server/src/entry/browser.tsx b/packages/react-server/src/entry/browser.tsx index 088ff2e94..57098316d 100644 --- a/packages/react-server/src/entry/browser.tsx +++ b/packages/react-server/src/entry/browser.tsx @@ -176,7 +176,10 @@ export async function start() { if (document.documentElement.dataset["noHydate"]) { reactDomClient.createRoot(document).render(reactRootEl); } else { - reactDomClient.hydrateRoot(document, reactRootEl); + reactDomClient.hydrateRoot(document, reactRootEl, { + // @ts-ignore TODO + formState: null, + }); } // custom event for RSC reload diff --git a/packages/react-server/src/entry/server.tsx b/packages/react-server/src/entry/server.tsx index e12369a6f..92f28f1f7 100644 --- a/packages/react-server/src/entry/server.tsx +++ b/packages/react-server/src/entry/server.tsx @@ -125,6 +125,8 @@ export async function renderHtml( let status = 200; try { ssrStream = await reactDomServer.renderToReadableStream(reactRootEl, { + // @ts-ignore TODO + formState: null, bootstrapModules: url.search.includes("__noJs") ? [] : assets.bootstrapModules, diff --git a/packages/react-server/src/features/server-action/client.tsx b/packages/react-server/src/features/server-action/client.tsx index 66589ba68..87c9b1d71 100644 --- a/packages/react-server/src/features/server-action/client.tsx +++ b/packages/react-server/src/features/server-action/client.tsx @@ -5,6 +5,7 @@ import { RedirectBoundary } from "../../runtime-client"; import { createError } from "../../server"; import { LayoutStateContext } from "../router/client"; +// TODO: replace with React.useActionState export function useActionData any>( action: T, ): Awaited> | undefined {