Skip to content

Commit

Permalink
4.3.1 - stabilized hook return with useMemo, bug fix with submitting …
Browse files Browse the repository at this point in the history
…state
  • Loading branch information
AlemTuzlak committed Mar 28, 2024
1 parent a4fda94 commit 85a584f
Show file tree
Hide file tree
Showing 3 changed files with 121 additions and 84 deletions.
5 changes: 5 additions & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,10 @@
"@typescript-eslint/no-unnecessary-type-constraint": "off",
"@typescript-eslint/no-explicit-any": "off",
"no-console": "error"
},
"settings": {
"react": {
"version": "18"
}
}
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "remix-hook-form",
"version": "4.3.0",
"version": "4.3.1",
"description": "Utility wrapper around react-hook-form for use with Remix.run",
"type": "module",
"main": "./dist/index.cjs",
Expand Down
198 changes: 115 additions & 83 deletions src/hook/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from "react";
import React, { useMemo } from "react";
import {
FetcherWithComponents,
SubmitFunction,
Expand Down Expand Up @@ -59,98 +59,130 @@ export const useRemixForm = <T extends FieldValues>({
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 =
navigation.state !== "idle" || (fetcher && fetcher.state !== "idle");
const isSubmittingForm = useMemo(
() =>
(navigation.state !== "idle" && navigation.formData !== undefined) ||
(fetcher && fetcher.state !== "idle" && fetcher.formData !== undefined),
[navigation.state, navigation.formData, fetcher?.state, fetcher?.formData],
);

// Submits the data to the server when form is valid
const onSubmit = (data: T) => {
setIsSubmittedSuccessfully(true);
const formData = createFormData(
{ ...data, ...submitData },
stringifyAllValues,
);
submit(formData, {
method: "post",
...submitConfig,
});
};
const onSubmit = useMemo(
() => (data: T) => {
setIsSubmittedSuccessfully(true);
const formData = createFormData(
{ ...data, ...submitData },
stringifyAllValues,
);
submit(formData, {
method: "post",
...submitConfig,
});
},
[submit, submitConfig, submitData, stringifyAllValues],
);

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

// React-hook-form uses lazy property getters to avoid re-rendering when properties
// that aren't being used change. Using getters here preservers that lazy behavior.
const formState: FormState<T> = {
get isDirty() {
return methods.formState.isDirty;
},
get isLoading() {
return methods.formState.isLoading;
},
get isSubmitted() {
return methods.formState.isSubmitted;
},
get isSubmitSuccessful() {
return isSubmittedSuccessfully || methods.formState.isSubmitSuccessful;
},
get isSubmitting() {
return isSubmittingForm || methods.formState.isSubmitting;
},
get isValidating() {
return methods.formState.isValidating;
},
get isValid() {
return methods.formState.isValid;
},
get disabled() {
return methods.formState.disabled;
},
get submitCount() {
return methods.formState.submitCount;
},
get defaultValues() {
return methods.formState.defaultValues;
},
get dirtyFields() {
return methods.formState.dirtyFields;
},
get touchedFields() {
return methods.formState.touchedFields;
},
get validatingFields() {
return methods.formState.validatingFields;
},
get errors() {
return methods.formState.errors;
},
};

return {
...methods,
handleSubmit: methods.handleSubmit(
submitHandlers?.onValid ?? onSubmit,
submitHandlers?.onInvalid ?? onInvalid,
),
reset: (
values?: T | DefaultValues<T> | undefined,
options?: KeepStateOptions,
) => {
setIsSubmittedSuccessfully(false);
methods.reset(values, options);
},
register: (
name: Path<T>,
options?: RegisterOptions<T> & {
disableProgressiveEnhancement?: boolean;
const formState: FormState<T> = useMemo(
() => ({
get isDirty() {
return methods.formState.isDirty;
},
) => ({
...methods.register(name, options),
...(!options?.disableProgressiveEnhancement && {
defaultValue: data?.defaultValues?.[name] ?? "",
get isLoading() {
return methods.formState.isLoading;
},
get isSubmitted() {
return methods.formState.isSubmitted;
},
get isSubmitSuccessful() {
return isSubmittedSuccessfully || methods.formState.isSubmitSuccessful;
},
get isSubmitting() {
return isSubmittingForm || methods.formState.isSubmitting;
},
get isValidating() {
return methods.formState.isValidating;
},
get isValid() {
return methods.formState.isValid;
},
get disabled() {
return methods.formState.disabled;
},
get submitCount() {
return methods.formState.submitCount;
},
get defaultValues() {
return methods.formState.defaultValues;
},
get dirtyFields() {
return methods.formState.dirtyFields;
},
get touchedFields() {
return methods.formState.touchedFields;
},
get validatingFields() {
return methods.formState.validatingFields;
},
get errors() {
return methods.formState.errors;
},
}),
[methods.formState, isSubmittedSuccessfully, isSubmittingForm],
);
const reset = useMemo(
() =>
(
values?: T | DefaultValues<T> | undefined,
options?: KeepStateOptions,
) => {
setIsSubmittedSuccessfully(false);
methods.reset(values, options);
},
[methods.reset],
);

const register = useMemo(
() =>
(
name: Path<T>,
options?: RegisterOptions<T> & {
disableProgressiveEnhancement?: boolean;
},
) => ({
...methods.register(name, options),
...(!options?.disableProgressiveEnhancement && {
defaultValue: data?.defaultValues?.[name] ?? "",
}),
}),
[methods.register, data?.defaultValues],
);

const handleSubmit = useMemo(
() =>
methods.handleSubmit(
submitHandlers?.onValid ?? onSubmit,
submitHandlers?.onInvalid ?? onInvalid,
),
[methods.handleSubmit, submitHandlers, onSubmit, onInvalid],
);

const hookReturn = useMemo(
() => ({
...methods,
handleSubmit,
reset,
register,
formState,
}),
formState,
};
[methods, handleSubmit, reset, register, formState],
);

return hookReturn;
};
interface RemixFormProviderProps<T extends FieldValues>
extends Omit<UseFormReturn<T>, "handleSubmit" | "reset"> {
Expand Down

0 comments on commit 85a584f

Please sign in to comment.