diff --git a/README.md b/README.md index 23ef58c..d3c1ddb 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,11 @@ End-to-end typesafe success, error & validation state control for Next.js 14 for - ✅ `
` component reads the action's response. - ✅ Computes progress meta-state like `isInvalid`, `isSuccess` and more. +**Context** + +- ✅ Access the action state via `useFormContext()` hook. +- ✅ Keeps JSX markup free of render-props. + ## Install ``` @@ -50,7 +55,7 @@ const i18nMiddleware = async () => { const authAction = formAction .use(i18nMiddleware) .use(async ({ ctx: { t } }) => - console.log("🎉 context enhanced by previous middlewares 🎉", t), + console.log("🎉 context enhanced by previous middlewares 🎉", t) ) .error(({ error }) => { if (error instanceof DbError) { @@ -85,7 +90,7 @@ export const signUp = authAction .refine((data) => data.password === data.confirm, { message: "Passwords don't match", path: ["confirm"], - }), + }) ) // if using refinement, only one input call is permited, as schema with ZodEffects is not extendable. .run(async ({ ctx: { t }, input: { email, password } }) => { // 🎉 passwords match! @@ -161,7 +166,7 @@ export const updateUser = createFormAction< return failure({ message: "Failed to update user." }); } - }, + } ); ``` @@ -206,6 +211,50 @@ export function UpdateUserForm() { } ``` +### Context Form + +```tsx +// Example route: /app/auth/signup/SignUpForm.tsx +"use client"; + +import { createForm, Pending, useFormContext } from "react-form-action/client"; +import { signupAction } from "./actions"; + +// This call will succeed only in client component +const { Form } = createForm(signupAction); + +function Error() { + // read any state from the FormContext: + const { + error, + data, + validationError, + isPending, + isFailure, + isInvalid, + isSuccess, + isInitial + } = useFormContext(); + + return isFailure && "Failed to submit". // use the error somehow +} + +// render this form on your RSC page (/app/auth/signup/page.tsx) +export function SignupForm() { + // This Form does not use render props. + return ( + + + + + {/* This renders only when the action is pending. */} +

Please wait...

+
+
+ ); +} +``` + ### `` Component Actions created with `formAction` builder will have the `validationError` of [`ZodFormattedError`](https://zod.dev/ERROR_HANDLING?id=formatting-errors) type. diff --git a/package-lock.json b/package-lock.json index 625e629..ea0d949 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "react-form-action", - "version": "1.2.3", + "version": "1.3.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "react-form-action", - "version": "1.2.3", + "version": "1.3.0", "license": "MIT", "devDependencies": { "@testing-library/dom": "^10.4.0", diff --git a/package.json b/package.json index f4a4bb8..86a3a02 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-form-action", - "version": "1.2.3", + "version": "1.3.0", "description": "State management helpers for the react form actions.", "repository": { "type": "git", diff --git a/src/FormContext.tsx b/src/FormContext.tsx index 77f6829..e9ca414 100644 --- a/src/FormContext.tsx +++ b/src/FormContext.tsx @@ -1,3 +1,5 @@ +"use client"; + import { createContext, use } from "react"; import type { FormState } from "./createFormAction"; import type { FormMetaState } from "./Form"; diff --git a/src/client.ts b/src/client.ts index c1c6970..907af86 100644 --- a/src/client.ts +++ b/src/client.ts @@ -1,4 +1,6 @@ "use client"; + +export * from "./createForm"; export * from "./Form"; export * from "./FormContext"; export * from "./FormStatus"; diff --git a/src/createForm.test.tsx b/src/createForm.test.tsx index 77f97c8..7c514ce 100644 --- a/src/createForm.test.tsx +++ b/src/createForm.test.tsx @@ -4,7 +4,7 @@ import { render, screen } from "@testing-library/react"; import { z } from "zod"; import { createForm } from "./createForm"; import { formAction } from "./formAction"; -import { useFormContext } from "."; +import { useFormContext } from "./FormContext"; describe("createForm", () => { test("it creates a Form which provides a context", async () => { diff --git a/src/createForm.tsx b/src/createForm.tsx index f0461a0..3b92437 100644 --- a/src/createForm.tsx +++ b/src/createForm.tsx @@ -1,3 +1,5 @@ +"use client"; + import React, { type PropsWithChildren } from "react"; import type { FormAction } from "./createFormAction"; import { Form as BaseForm } from "./Form"; diff --git a/src/index.ts b/src/index.ts index 8967e10..e798621 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,3 +1,2 @@ -export * from "./createForm"; export * from "./createFormAction"; export * from "./formAction";