Skip to content

Commit

Permalink
feat(createForm): Build form with a context
Browse files Browse the repository at this point in the history
  • Loading branch information
MiroslavPetrik committed Dec 19, 2024
1 parent ab7801e commit 658fe5e
Show file tree
Hide file tree
Showing 8 changed files with 62 additions and 8 deletions.
55 changes: 52 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ End-to-end typesafe success, error & validation state control for Next.js 14 for
-`<Form />` 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

```
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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!
Expand Down Expand Up @@ -161,7 +166,7 @@ export const updateUser = createFormAction<

return failure({ message: "Failed to update user." });
}
},
}
);
```

Expand Down Expand Up @@ -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 (
<Form>
<input name="email" />
<input name="password" />
<Pending>
{/* This renders only when the action is pending. */}
<p>Please wait...</p>
</Pending>
</Form>
);
}
```

### `<ZodFieldError>` Component

Actions created with `formAction` builder will have the `validationError` of [`ZodFormattedError`](https://zod.dev/ERROR_HANDLING?id=formatting-errors) type.
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down
2 changes: 2 additions & 0 deletions src/FormContext.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
"use client";

import { createContext, use } from "react";
import type { FormState } from "./createFormAction";
import type { FormMetaState } from "./Form";
Expand Down
2 changes: 2 additions & 0 deletions src/client.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
"use client";

export * from "./createForm";
export * from "./Form";
export * from "./FormContext";
export * from "./FormStatus";
Expand Down
2 changes: 1 addition & 1 deletion src/createForm.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 () => {
Expand Down
2 changes: 2 additions & 0 deletions src/createForm.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
"use client";

import React, { type PropsWithChildren } from "react";
import type { FormAction } from "./createFormAction";
import { Form as BaseForm } from "./Form";
Expand Down
1 change: 0 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
export * from "./createForm";
export * from "./createFormAction";
export * from "./formAction";

0 comments on commit 658fe5e

Please sign in to comment.