Skip to content

Commit

Permalink
feat: useNativeValidity
Browse files Browse the repository at this point in the history
  • Loading branch information
airjp73 committed Jun 15, 2024
1 parent a3131cb commit 78c9797
Show file tree
Hide file tree
Showing 6 changed files with 67 additions and 8 deletions.
10 changes: 3 additions & 7 deletions apps/docs-v2/app/examples/simple-form-rvf/react.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useForm } from "@rvf/react";
import { useEffect, useRef, useState } from "react";
import { useForm, useNativeValidity } from "@rvf/react";
import { useRef } from "react";
import { Button } from "~/ui/button";
import { Input } from "~/ui/input";
import { Label } from "~/ui/label";
Expand Down Expand Up @@ -36,11 +36,7 @@ export const SignupForm = () => {
});

const confirmRef = useRef<HTMLInputElement>(null);
const passwordMatchError = form.error("confirmPassword") ?? "";
useEffect(
() => confirmRef.current?.setCustomValidity(passwordMatchError),
[passwordMatchError],
);
useNativeValidity(confirmRef, form.error("confirmPassword"));

return (
<form {...form.getFormProps()}>
Expand Down
15 changes: 14 additions & 1 deletion apps/docs-v2/app/routes/_docs.simple-form.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,23 @@ Notice how much we have to:

- Manually pull all the data out of the form
- Manually validate that the passwords match in three different places
- Manually call `setCustomValidity` and `reportValidity` on the confirm password input
- Manually manage a loading state

## Simplifying with RVF

Now let's add just a little bit of RVF.

<SimpleFormRvf />
<SimpleFormRvf />

We've only saved a few lines of code, but the code is much less manual and more organized.
Now we

- Get the data directly in our submit handler
- Get to keep our existing, native validations via the `required` prop.
- Get our custom validation run automatically at the right time
- Don't have to call `setCustomValidity` and `reportValidity` directly
- Don't have to manage a loading state

And we didn't have to call `register` or `useField` or use a `Field` component or anything.
Just `useForm`, some native validations, and `useNativeValidity` for an extra custom error.
1 change: 1 addition & 0 deletions packages/react/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,4 @@ export {
useFormScopeOrContext,
} from "./context";
export { useFormScope } from "./useFormScope";
export { useNativeValidity } from "./useNativeValidity";
37 changes: 37 additions & 0 deletions packages/react/src/test/use-native-validity.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { useRef, useState } from "react";
import { useNativeValidity } from "../useNativeValidity";
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";

it("should setCustomValidity on the input when the error changes", async () => {
const Comp = () => {
const ref = useRef<HTMLInputElement>(null);
const [error, setError] = useState("test");
useNativeValidity(ref, error);
return (
<div>
<input ref={ref} data-testid="input" />
<button
type="button"
onClick={() => setError("")}
data-testid="clear"
/>
<button
type="button"
onClick={() => setError("jimbo")}
data-testid="change"
/>
</div>
);
};

render(<Comp />);

expect(screen.getByTestId("input")).toBeInvalid();

await userEvent.click(screen.getByTestId("clear"));
expect(screen.getByTestId("input")).toBeValid();

await userEvent.click(screen.getByTestId("change"));
expect(screen.getByTestId("input")).toBeInvalid();
});
11 changes: 11 additions & 0 deletions packages/react/src/useNativeValidity.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { RefObject, useEffect } from "react";

export const useNativeValidity = (
ref: RefObject<HTMLElement & { setCustomValidity: (error: string) => void }>,
error?: string | null,
) => {
useEffect(() => {
if (!ref.current) return;
ref.current.setCustomValidity(error ?? "");
}, [error, ref]);
};
1 change: 1 addition & 0 deletions packages/remix/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export {
useFormScopeOrContext,
isValidationErrorResponse,
useFormScope,
useNativeValidity,
} from "@rvf/react";
export { useForm, type RemixFormOpts as FormScopeRemixOpts } from "./useForm";
export { ValidatedForm, type ValidatedFormProps } from "./ValidatedForm";
Expand Down

0 comments on commit 78c9797

Please sign in to comment.