Handling form submission

We already have a submit button in the Form component that submits the form. We aren't handling the form submission yet though. Let's handle form submission, which includes a call to a function passed in by the consumer:

  1. In Form.tsx, let's import the FormEvent type from React:
import { FC, useState, createContext, FormEvent } from 'react';
  1. Let's add an event listener for the submit event in the Form component JSX:
<form noValidate={true} onSubmit={handleSubmit}>
...
</form>

When the submit button is clicked on the form, the submit event will be triggered and a function called handleSubmit will be invoked.

  1. Let's start to implement the handleSubmit function just beneath the validate function:
const handleSubmit = async (e: FormEvent<HTMLFormElement>) => {
e.preventDefault();
if (validateForm()) {
// TODO - set state to indicate submission is in progress
// TODO - call the consumer submit function
// TODO - set any errors in state
// TODO - set state to indicate submission has finished
}
};

So, we prevent the default form submission from happening using the preventDefault method in the event parameter. We do this to prevent a full page postback to the server that we don't want. We also call a function, validateForm, to validate the whole form, which we still need to implement.

Notice that we have included the async keyword before the function parameters to indicate that the function is asynchronous. This is because the consumers submit a handler function that is likely to call a web service, which is asynchronous. 

  1. Let's implement the validateForm function beneath the handleSubmit function:
const validateForm = () => {
const newErrors: Errors = {};
let haveError: boolean = false;
if (validationRules) {
Object.keys(validationRules).forEach(fieldName => {
newErrors[fieldName] = validate(fieldName);
if (newErrors[fieldName].length > 0) {
haveError = true;
}
});
}
setErrors(newErrors);
return !haveError;
};

So, we iterate through the validation rules for each field, invoking each rule and updating the errors state. We also return whether any errors were found.

  1. Let's create some additional state for the submission process:
const [values, setValues] = useState<Values>({});
const [errors, setErrors] = useState<Errors>({});
const [touched, setTouched] = useState<Touched>({});
const [submitting, setSubmitting] = useState(false);
const [submitted, setSubmitted] = useState(false);
const [submitError, setSubmitError] = useState(false);

The submitting state indicates whether the submission is in progress, the submitted state indicates whether the form has been submitted, and the submitError state indicates whether the submission failed.

  1. Moving back to the handleSubmit function, let's set these state values:
const handleSubmit = async (e: FormEvent<HTMLFormElement>) => {
e.preventDefault();
if (validateForm()) {
setSubmitting(true);
setSubmitError(false);
// TODO - call the consumer submit function
// TODO - set any errors in state
setSubmitting(false);
setSubmitted(true);
}
};
  1. We now need to call a function from the consumer that will do the actual form submission. So, let's create a function prop called onSubmit for this purpose:
export interface SubmitResult {
success: boolean;
errors?: Errors;
}

interface Props {
submitCaption?: string;
validationRules?: ValidationProp;
onSubmit: (values: Values) => Promise<SubmitResult>;
}

The function will be passed the field values from the form and will return an object containing whether the submission was successful and any errors that occurred.

  1. Let's destructure the onSubmit prop in the Form component:
export const Form: FC<Props> = ({
submitCaption,
children,
validationRules,
onSubmit
}) => { ... }
  1. Moving back to the handleSubmit function again, let's call the onSubmit function that has been passed in:
const handleSubmit = async (e: FormEvent<HTMLFormElement>) => {
e.preventDefault();
if (validateForm()) {
setSubmitting(true);
setSubmitError(false);
const result = await onSubmit(values);
setErrors(result.errors || {});
setSubmitError(!result.success);
setSubmitting(false);
setSubmitted(true);
}
};

So, we asynchronously call the onSubmit function, update the errors state from the submission result, and set the submitError state accordingly.

  1. In the Form components JSX, let's disable the form when submission is in progress or the form has been successfully submitted:
<form noValidate={true} onSubmit={handleSubmit}>
<fieldset
disabled={submitting || (submitted && !submitError)}
css={ ... }
>
...
</fieldset>
</form>
  1. We want to inform the user of whether the submission has been successful or not. We also want the consumer to be able to pass in the success and failure messages. Let's add these to the props interface:
interface Props {
submitCaption?: string;
validationRules?: ValidationProp;
onSubmit: (values: Values) => Promise<SubmitResult>;
successMessage?: string;
failureMessage?: string;
}
  1. Destructure these in the Form component parameter with some sensible defaults:
export const Form: FC<Props> = ({
submitCaption,
children,
validationRules,
onSubmit,
successMessage = 'Success!',
failureMessage = 'Something went wrong'
}) => { ... }
  1. We can now add these messages to the JSX with the appropriate conditions under the submit button:
<fieldset ... >
{children}
<div ... >
<PrimaryButton type="submit">{submitCaption}</PrimaryButton>
</div>
{submitted && submitError && (
<p css={css`color: red;`}>
{failureMessage}
</p>
)}
{submitted && !submitError && (
<p css={css`color: green;`}>
{successMessage}
</p>
)}
</fieldset>

That completes the submission logic in the Form component.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.16.137.108