Skip to content

Commit

Permalink
4.0.0 release - error handling
Browse files Browse the repository at this point in the history
  • Loading branch information
AlemTuzlak committed Jan 13, 2024
1 parent e2ead74 commit e740d37
Show file tree
Hide file tree
Showing 7 changed files with 82 additions and 136 deletions.
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export const action = async ({ request }: ActionFunctionArgs) => {
await getValidatedFormData<FormData>(request, resolver);
if (errors) {
// The keys "errors" and "defaultValue" are picked up automatically by useRemixForm
return json({errors, defaultValues});
return json({ errors, defaultValues });
}

// Do something with the data
Expand Down Expand Up @@ -100,7 +100,7 @@ export const action = async ({ request }: ActionFunctionArgs) => {
// validate the form data
const { errors, data } = await validateFormData(formData, resolver);
if (errors) {
return json(errors, {
return json({ errors }, {
status: 422,
});
}
Expand Down Expand Up @@ -136,7 +136,7 @@ export const action = async ({ request }: ActionFunctionArgs) => {
const { errors, data } =
await getValidatedFormData<FormData>(request, resolver);
if (errors) {
return json(errors);
return json({ errors });
}
// Do something with the data
};
Expand Down Expand Up @@ -175,7 +175,7 @@ export const action = async ({ request }: ActionFunctionArgs) => {
const { errors, data } =
await validateFormData<FormData>(formData, resolver);
if (errors) {
return json(errors);
return json({ errors });
}
// Do something with the data
};
Expand Down
79 changes: 65 additions & 14 deletions package-lock.json

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

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "remix-hook-form",
"version": "3.2.3",
"version": "4.0.0",
"description": "Utility wrapper around react-hook-form for use with Remix.run",
"type": "module",
"main": "./dist/index.cjs",
Expand Down Expand Up @@ -87,7 +87,7 @@
"prettier": "^3.0.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-hook-form": "^7.48.2",
"react-hook-form": "^7.49.3",
"remix-development-tools": "^3.1.1",
"rollup": "^3.20.2",
"rollup-plugin-typescript2": "^0.34.1",
Expand Down
19 changes: 5 additions & 14 deletions src/hook/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import type {
UseFormProps,
UseFormReturn,
} from "react-hook-form";
import { createFormData, mergeErrors } from "../utilities";
import { createFormData } from "../utilities";

export type SubmitFunctionOptions = Parameters<SubmitFunction>[1];

Expand Down Expand Up @@ -54,7 +54,7 @@ export const useRemixForm = <T extends FieldValues>({
const actionData = useActionData();
const submit = fetcher?.submit ?? actionSubmit;
const data: any = fetcher?.data ?? actionData;
const methods = useForm<T>(formProps);
const methods = useForm<T>({ ...formProps, errors: data?.errors });
const navigation = useNavigation();
// Either it's submitted to an action or submitted to a fetcher (or neither)
const isSubmittingForm =
Expand All @@ -72,13 +72,10 @@ export const useRemixForm = <T extends FieldValues>({
...submitConfig,
});
};
const values = methods.getValues();
const validKeys = Object.keys(values);

// eslint-disable-next-line @typescript-eslint/no-empty-function
const onInvalid = () => {};

const formState = methods.formState;

const {
dirtyFields,
isDirty,
Expand All @@ -92,13 +89,7 @@ export const useRemixForm = <T extends FieldValues>({
errors,
isLoading,
disabled,
} = formState;

const formErrors = mergeErrors<T>(
errors,
data?.errors ? data.errors : data,
validKeys,
);
} = methods.formState;

return {
...methods,
Expand Down Expand Up @@ -133,7 +124,7 @@ export const useRemixForm = <T extends FieldValues>({
touchedFields,
submitCount,
isLoading,
errors: formErrors,
errors,
},
};
};
Expand Down
5 changes: 4 additions & 1 deletion src/testing-app/app/routes/_index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -99,13 +99,16 @@ export default function Index() {
},
});
const { register, handleSubmit, formState, watch, setError } = methods;
setError("root.test", { type: "manual", message: "test" });

return (
<RemixFormProvider {...methods}>
<p>Add a thing...</p>

<Form method="post" encType="multipart/form-data" onSubmit={handleSubmit}>
<input type="file" {...register("file")} />
{formState.errors.file && (
<p className="error">{formState.errors.file.message}</p>
)}
<div>
<button type="submit" className="button">
Add
Expand Down
57 changes: 0 additions & 57 deletions src/utilities/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import {
getFormDataFromSearchParams,
getValidatedFormData,
isGet,
mergeErrors,
parseFormData,
validateFormData,
} from "./index";
Expand Down Expand Up @@ -113,62 +112,6 @@ describe("parseFormData", () => {
});
});

describe("mergeErrors", () => {
it("should return the backend errors if frontend errors is not provided", () => {
const backendErrors: any = {
username: { message: "This field is required" },
};
const mergedErrors = mergeErrors({}, backendErrors);
expect(mergedErrors).toEqual(backendErrors);
});

it("should return the frontend errors if backend errors is not provided", () => {
const frontendErrors: any = { email: { message: "Invalid email" } };
const mergedErrors = mergeErrors(frontendErrors, undefined);
expect(mergedErrors).toEqual(frontendErrors);
});

it("should merge nested objects recursively", () => {
const frontendErrors: any = {
password: { message: "Password is required" },
confirmPassword: { message: "Passwords do not match" },
profile: { firstName: { message: "First name is required" } },
};
const backendErrors: any = {
confirmPassword: { message: "Password confirmation is required" },
profile: {
lastName: { message: "Last name is required" },
address: { street: { message: "Street is required" } },
},
};
const expectedErrors = {
password: { message: "Password is required" },
confirmPassword: { message: "Password confirmation is required" },
profile: {
firstName: { message: "First name is required" },
lastName: { message: "Last name is required" },
address: { street: { message: "Street is required" } },
},
};
const mergedErrors = mergeErrors(frontendErrors, backendErrors);
expect(mergedErrors).toEqual(expectedErrors);
});

it("should overwrite the frontend error message with the backend error message", () => {
const frontendErrors: any = {
username: { message: "This field is required" },
};
const backendErrors: any = {
username: { message: "The username is already taken" },
};
const expectedErrors = {
username: { message: "The username is already taken" },
};
const mergedErrors = mergeErrors(frontendErrors, backendErrors);
expect(mergedErrors).toEqual(expectedErrors);
});
});

describe("generateFormData", () => {
it("should generate an output object for flat form data", () => {
const formData = new FormData();
Expand Down
Loading

0 comments on commit e740d37

Please sign in to comment.