How to create a simple formik field for entering date and time?

Viewed 2849

I am trying to create a very simple formik field for entering date and time. The requirements are:

  1. The field's value should be a JavaScript Date.
  2. The field should allow entering free-form text in an input field and convert it to a Date on blur events.
  3. The field should display an error if the entered string cannot be converted to a valid date.
  4. If the form is reset, the field should show the initial value of the date.

While I was able to satisfy requirements 1 & 2 quite easily, I am having trouble with requirements 3 & 4. I think part of the problem is that I am storing the raw text of the input field in internal state. However, I can't think of another way. Is there a better pattern?

Here's my CodeSandbox. Please see the sections marked with "Issue 1" & "Issue 2"

1 Answers

Try this solution:

  1. Use Field component from Formik to bind your custom component
<Field name="date" timezone={DefaultTz} component={DateTimeField} />
  1. Use form property from Field to display errors
{form.errors.date ? (
  <div className="text-danger">{form.errors.date}</div>
) : null}
  1. Use spread operator to bind formik fields and your custom blur which converts the date
<input
  className="form-control"
  placeholder={format}
  {...{ ...field, onBlur }}
/>

Altogether it looks like this

import React from "react";
import { DateUtils } from "@react-force/date-utils";

export interface DateTimeFieldProps extends React.InputHTMLAttributes<Date> {
  field: any;
  form: any;
  timezone: string;
  format?: string;
}

export const DateTimeField = ({
  field,
  form,
  timezone,
  format = "YYYY-MM-DD hh:mm A",
}: DateTimeFieldProps) => {
  const onBlur = () => {
    try {
      form.setValues({
        date: DateUtils.createDate(field.value, format, timezone),
      });
    } catch (error) {
      console.log(error);
    }
  };

  return (
    <React.Fragment>
      <input
        className="form-control"
        placeholder={format}
        {...{ ...field, onBlur }}
      />
      {form.errors.date ? (
        <div className="text-danger">{form.errors.date}</div>
      ) : null}
    </React.Fragment>
  );
};

The working version is here

Related