Skip to content

Commit

Permalink
Guard against setting state after a promise finishes
Browse files Browse the repository at this point in the history
Validation and submission are async. In the time that they're running,
the component might unmount. It's fairly common to navigate after a
successful submission, for example.
  • Loading branch information
rzane committed Aug 7, 2020
1 parent 75f5ea9 commit bda2b41
Showing 1 changed file with 21 additions and 20 deletions.
41 changes: 21 additions & 20 deletions src/useForm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,18 @@ import { useIdentifier } from "./useIdentifier";
import { FormOptions, Form } from "./types";
import { getAllTouched, useEventCallback } from "./utilities";

function useMounted() {
const ref = useRef<boolean>(true);

useEffect(() => {
return () => {
ref.current = false;
};
}, []);

return ref.current;
}

/**
* Create a new form. A form requires an initial value, a function to validate,
* and a submit handler.
Expand Down Expand Up @@ -42,28 +54,24 @@ export function useForm<Value, Result = Value>(
const initialError = useRef(options.initialError).current;
const initialTouched = useRef(options.initialTouched).current;

const isMounted = useMounted();
const [value, setValue] = useState(initialValue);
const [error, setError] = useState(initialError);
const [touched, setTouched] = useState(initialTouched);
const [isSubmitting, setSubmitting] = useState(false);
const [isValidating, setValidating] = useState(false);

const executeValidate = async (touchAllErrors: boolean) => {
const executeValidate = async (isSubmit: boolean) => {
setValidating(true);

try {
const result = await runValidate(value);
const errors = result.valid ? undefined : result.error;

// When submitting, touch all fields that have errors.
if (touchAllErrors) {
setTouched(getAllTouched(errors));
}

setError(errors);
if (isMounted && isSubmit) setTouched(getAllTouched(errors));
if (isMounted) setError(errors);
return result;
} finally {
setValidating(false);
if (isMounted) setValidating(false);
}
};

Expand All @@ -76,12 +84,9 @@ export function useForm<Value, Result = Value>(

try {
const result = await executeValidate(true);

if (result.valid) {
await runSubmit(result.value);
}
if (result.valid) await runSubmit(result.value);
} finally {
setSubmitting(false);
if (isMounted) setSubmitting(false);
}
});

Expand All @@ -93,15 +98,11 @@ export function useForm<Value, Result = Value>(
);

useEffect(() => {
if (validateOnChange) {
validate();
}
if (validateOnChange) validate();
}, [value, validateOnChange, validate]);

useEffect(() => {
if (validateOnBlur) {
validate();
}
if (validateOnBlur) validate();
}, [touched, validateOnBlur, validate]);

return useMemo(
Expand Down

0 comments on commit bda2b41

Please sign in to comment.