I'm using react for the first time. I have hooked up a form with Formik and have all the validation stuff working but I am stuck on keeping form values if there was a problem.
The form collects data to add a record to the database. If the API response has data, the insert was successful and I show a success Alert. If the insert failed, data is empty because null was returned from the API and I show a danger Alert.
My question is how do I keep the form fields' values if there was an error? I want to close the danger Alert and still have the form fields populated so the user does not have to start over. I don't need to do this on success because the information the user entered got into the database.
I feel like this has something to do with the state of the form itself or the fields but I can't figure how to retain the values. I tried doing ...values in onSubmit but that didn't work (don't quite understand that spread thing). Any help would be appreciated.
This is what I have:
import React, { useState } from "react"
import * as Yup from "yup"
import { Formik, Form, Field } from "formik"
import "../../custom.css"
import * as apiService from "./../../services/apiService"
import DatePicker from "../../utils/DatePicker"
import "react-datepicker/dist/react-datepicker.css"
import Alert from "reactstrap/lib/Alert"
import { Button } from "reactstrap"
import "bootstrap/dist/css/bootstrap.min.css"
const CompanyMasterAdd = () => {
  const [showAlert, setShowAlert] = useState(false)
  const [apiResponse, setApiResponse] = useState(null)
  const urlRegex =
    /((([A-Za-z]{3,9}:(?:\/\/)?)(?:[-;:&=\\+\\$,\w]+@)?[A-Za-z0-9.-]+|(?:www.|[-;:&=\\+\\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\\+~%\\/.\w-_]*)?\??(?:[-\\+=&;%@.\w_]*)#?(?:[\w]*))?)/
  //Setup values for drop downs so we can validate against selection
  //This one is set here because we need to access statuses in the YUP validation
  const statuses = ["1", "2", "3", "4", "5"]
  const statusOptions = statuses.map((statusID, key) => (
    <option value={statusID} key={key}>
      {statusID}
    </option>
  ))
  //setup validation requirements and error messages for fields via Yup
  const companyMasterAddSchema = Yup.object().shape({
    companyId: Yup.number()
      //custom required message when required field not filled in
      .required("Company Id is required")
      //if it's not a number, display custom error message
      .typeError("Company Id must be a number")
      .positive()
      .integer(),
    companyName: Yup.string()
      .max(75, "Must be 75 characters or less")
      .required("Company Name is required"),
    databaseName: Yup.string()
      .required("Database Name is required")
      .max(100, "Must be 100 characters or less"),
    statusID: Yup.string().required("Status ID is required").oneOf(statuses),
    website: Yup.string().matches(urlRegex, "Website URL is not valid"),
  })
  //Setup values for drop downs so we can validate against selection
  const companyTypes = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]
  const companyTypeOptions = companyTypes.map((companyTypeID, key) => (
    <option value={companyTypeID} key={key}>
      {companyTypeID}
    </option>
  ))
  //dismiss alert when button is closed
  const onDismiss = () => {
    setShowAlert(false)
  }
  return (
    <div id="companyMasterPageContainer">
      <h1>Add Company Master record</h1>
      <hr />
      {showAlert ? (
        <div>
          <Alert
            color={apiResponse && !apiResponse.data ? "danger" : "success"}
          >
            <h4 className="alert-heading">
              {apiResponse && !apiResponse.data ? "ERROR" : "SUCCESS"}
            </h4>
            <p>
              {apiResponse && !apiResponse.data
                ? 
                //TODO: keep form data populated if there is a failure
                //TODO: get the actual error from the API somehow
                "An error occurred. Check the ProcedureErrorLog table"
                : "Record added"}
            </p>
            <hr />
            <Button onClick={() => onDismiss()}>Dismiss</Button>
          </Alert>
        </div>
      ) : (
        // show form if showAlert is false
        <div id="companyMasterAddFormContainer">
          <h2>Form area</h2>
          <Formik
            initialValues={{
              companyId: "",
              companyName: "",
              website: "",
              fiscalYearEnd: null,
              taxIDNumber: "",
              companyTypeID: "",
              isKeyCompany: false,
              databaseName: "",
              statusID: "",
            }}
            validationSchema={companyMasterAddSchema}
            onSubmit={async (
              values,
              { resetForm, setSubmitting, isSubmitting }
            ) => {
              apiService.addCompanyMaster(values).then((response) => {
                setApiResponse(response)
                setShowAlert(true)
              })
            }}
          >
            {({ errors, touched, values, setFieldValue }) => (
              <Form id="companyMasterAddForm">
                <div>
                  <span className="requiredStar">
                    * indicates a required field
                  </span>
                </div>
                <div className="row">
                  <div className="formLabel">
                    <label
                      htmlFor="companyId"
                      className="companyMasterAddFormLabel"
                    >
                      Company ID <span className="requiredStar">*</span>
                    </label>
                  </div>
                  <div className="formInput">
                    <Field
                      value={values.companyId}
                      type="text"
                      id="companyId"
                      className="companyMasterAddFormField"
                      name="companyId"
                      placeholder="e.g., 1,2,3..."
                    />
                    {/* If this field has been touched, and it contains an error, display it  */}
                    {touched.companyId && errors.companyId && (
                      <div className="formError">{errors.companyId}</div>
                    )}
                  </div>
                </div>
                <div className="row">
                  <div className="formLabel">
                    <label
                      className="companyMasterAddFormLabel"
                      htmlFor="fiscalYearEnd"
                    >
                      Fiscal Year End
                    </label>
                  </div>
                  <div className="formInput">
                    <DatePicker
                      name="fiscalYearEnd"
                      value={values.fiscalYearEnd}
                      onChange={setFieldValue}
                      className="companyMasterAddFormField"
                      class="companyMasterAddFormField"
                    />
                    {/* If this field has been touched, and it contains an error, display it  */}
                    {touched.fiscalYearEnd && errors.fiscalYearEnd && (
                      <div className="formError">{errors.fiscalYearEnd}</div>
                    )}
                  </div>
                </div>
                <div className="row">
                  <button
                    type="submit"
                    style={{
                      textAlign: "center",
                      marginLeft: "30%",
                      marginRight: "30%",
                    }}
                  >
                    Submit
                  </button>
                </div>
              </Form>
            )}
          </Formik>
        </div>
      )}
    </div>
  )
}
export default CompanyMasterAdd
UPDATE - GOT IT WORKING
Separated the conditional logic for the alert and the form:
{
        showAlert && (
          <div>
            <Alert
              color={apiResponse && !apiResponse.data ? "danger" : "success"}
            >
              <h4 className="alert-heading">
                {apiResponse && !apiResponse.data ? "ERROR" : "SUCCESS"}
              </h4>
              <p>
                {apiResponse && !apiResponse.data
                  ? //TODO: keep form data populated if there is a failure
                    "An error occurred. Check the xyztable in the database"
                  : "Record added"}
              </p>
              <hr />
              <Button onClick={() => onDismiss()}>Dismiss</Button>
            </Alert>
          </div>
        )
        // show form if showAlert is false
      }
      <div
        id="companyMasterAddFormContainer"
        style={{ visibility: open ? "visible" : "hidden" }}
      >
        <h2>Form area</h2>
        <Formik
          initialValues={{
BONUS - HIDE FORM WHILE ALERT IS BEING DISPLAYED
- Added new state variable:
const [open, setOpen] = useState(true)
- Added visibility style in the div container for the form:
<div
        id="companyMasterAddFormContainer"
        style={{ visibility: open ? "visible" : "hidden" }}
      >
- Toggle the state of open in the onSubmit area of form:
onSubmit={async (
            values,
            { resetForm, setSubmitting, isSubmitting }
          ) => {
            setSubmitting(false)
            apiService.addCompanyMaster(values).then((response) => {
              setApiResponse(response)
              setShowAlert(true)
              setOpen(false)
              if (response && response.data) {
                resetForm()
              }
            })
- Toggle the state on the alert dismissal:
const onDismiss = () => {
    setShowAlert(false)
    setOpen(true)
  }
 
    