51

I'm trying to use react-datepicker in a Formik form.

I have:

import DatePicker from "react-datepicker";
import "react-datepicker/dist/react-datepicker.css";


class Fuate extends React.Component {
    state = {
        dueDate: new Date()

    }

<Formik
                initialValues={initialValues}
                validationSchema={Yup.object().shape({
                    title: Yup.string().required("A title is required "),
                })}

                onSubmit={this.handleSubmit}

                render={({ 
                    errors, 
                    status, 
                    touched, 
                    setFieldValue,
                    setFieldTouched, 
                    handleChange, 
                    handleBlur,
                    handleSubmit, 
                    isSubmitting, 
                    dirty, 
                    values 
                }) => {

                return (
                    <div>
            ...

<DatePicker
                                            name={'dueDate'}
                                            value={values['dueDate']}
                                            onChange={e => setFieldValue('dueDate', e)}
                                        />
                                        <DatePicker
                                        style={{ width: 180 }}
                                        date={values.dueDate}
                                        mode="date"
                                        format="YYYY-MM-DD"
                                        minDate={Date.now.toString()}
                                        maxDate="2050-06-01"
                                        confirmBtnText="Confirm"
                                        cancelBtnText="Cancel"
                                        showIcon={false}
                                        customStyles={{
                                            dateInput: {
                                            marginLeft: 0,
                                            borderColor: "#fff"
                                            }
                                        }}
                                        onDateChange={date => setFieldValue("dueDate", date)}
                                        onTouch={setFieldTouched}
                                        />

For both of these options, the form renders, I can select a date on the calendar but it does not appear in the box and the state value is not updated with the selection.

There are no errors in the console, but the warning says:

Starting with v2.0.0-beta.1 date-fns doesn't accept strings as arguments. Please use parseISO to parse strings. See: toDate @ index.js:45

I tried making the initial state:

dueDate: new Date().toISOString(),

but it makes no difference.

I've seen lots of posts about setting this up with Antd's date picker, but can't find instructions for how to do it with react-datepicker.

2
  • Never mind everyone. Ive given up trying to figure this out. I found react-formik-ui an it works just fine Commented May 26, 2019 at 10:22
  • 6
    Still answering because this question is valid and may be useful for someone with same query in future. Commented May 28, 2019 at 13:44

6 Answers 6

75

Update to Dani Vijay's answer.
This uses useField and useFormikContext from Formik v2, to simplify usage of the component.

DatePicker.jsx:

import React from "react";
import { useField, useFormikContext } from "formik";
import DatePicker from "react-datepicker";

export const DatePickerField = ({ ...props }) => {
  const { setFieldValue } = useFormikContext();
  const [field] = useField(props);
  return (
    <DatePicker
      {...field}
      {...props}
      selected={(field.value && new Date(field.value)) || null}
      onChange={val => {
        setFieldValue(field.name, val);
      }}
    />
  );
};

Usage (see Dani's answer for the complete form declaration):

...
<DatePickerField name="date" />
...

Code at codesandbox

Sign up to request clarification or add additional context in comments.

15 Comments

This is the best answer now (as of end of 2019)
There is also a direct setValue helper. const [field,, { setValue }] = useField(props); and then onChange={val => setValue(val)}
Thanks no worries, I figured out my issue. I was referring to libraries like React-Bootstrap or Material-UI setting the is-invalid or error class on the React-Datapicker, similar to other controls, to enable common styling and error messages. To support that, we just need to pass to this wrapper the isInvalid or error prop, and manually set className , e.g. className={"form-control " + props.isInvalid ? "is-invalid" : ""}. In other words, there's a bit more manual work to make the datepicker fit those React component libraries in terms of error styling.
I found an issue with Formik/Yup and this solution. Any non-Required validation requires re-touching the datepicker to be propagated, while Required validation is triggered right away. It could be a Yup issue. I created a thread on this: stackoverflow.com/questions/67662804/…
One thing confused me initially - using this, you don't add onChange and value props, you don't need them, it works w/o them.
|
30

react-datepicker can used with Formik by utilising setFieldValue,

const DatePickerField = ({ name, value, onChange }) => {
    return (
        <DatePicker
            selected={(value && new Date(value)) || null}
            onChange={val => {
                onChange(name, val);
            }}
        />
    );
};

const App = () => (
    <Formik
        initialValues={{ date: "" }}
        ...
    >
        {props => {
            const {
                values,
                handleSubmit,
                setFieldValue
                ...
            } = props;
            return (
                <form onSubmit={handleSubmit}>
                    <DatePickerField
                        name="date"
                        value={values.date}
                        onChange={setFieldValue}
                    />
                    ...

CodeSandbox demo here

3 Comments

I don't know why I had to do that : onChange={(val) => setFieldValue('date', val)}
@DamienRomito - my guess: If you use DatePicker directly, that is what you need to do. This answer moves that logic into the DatePickerField component, which is a wrapper around DatePicker. There, it can use the name parameter, so don't have to hard-code the name 'date'.
@DaniVijay - using Formik v2's useFormikContext and useField, the usage of DatePickerField can be simplified, so don't need to explicitly pass in value and onChange on each call. Updated demo. Usage: <DatePicker name="date" />. my answer.
4

Update to Dani Vijay and ToolmakerSteve's answers:

Use setValue directly instead of setFieldValue. Based on @asologor's comment.

import React from "react";
import { useField } from "formik";
import DatePicker from "react-datepicker";

export const DatePickerField = ({ ...props }) => {
  const [field, , { setValue }] = useField(props);
  return (
    <DatePicker
      {...field}
      {...props}
      selected={(field.value && new Date(field.value)) || null}
      onChange={(val) => {
        setValue(val);
      }}
    />
  );
};

Note: This method lets you to use Formik with react-datepicker, but it also works for other UI Libraries like Material-UI and Ant Design too.

1 Comment

This answer was useful for working with NextJs, being used as <DatePickerField minDate={new Date()} dateFormat="yyyy/MM/dd" id="date_run" placeholderText="Enter date yyyy/mm/dd" {...formik.getFieldProps('date_run')}/>. However loading the previously saved data 2022-02-23T22:00:00.000Z does not automatically switch to the dateFormat (yyyy/MM/dd), but stays in the raw date/time format.
3
const formik = useFormik({
    initialValues: {    
     date:""
    },
    onSubmit: (values) => {
      alert(JSON.stringify(values, null, 2));
    },
  }); 

 <MuiPickersUtilsProvider utils={DateFnsUtils}>
                  <KeyboardDatePicker
                    disableToolbar
                    variant="inline"
                    TextFieldComponent={(params) => {
                      return <TextField className={classes.TextField} 
                       {...params} variant="outlined" />;
                    }}
                    format="dd MMM yyyy"
                    margin="normal"
                    name="installDate"
                    value={formik.values.date}
                    onChange={(newDate) => {

                       //use this here
                      formik.setFieldValue("date", newDate);


                    }}
                    KeyboardButtonProps={{
                      "aria-label": "change date",
                    }}
                  />
                </MuiPickersUtilsProvider>

Comments

2

What I see from your code is DatePicker is not inside of Formik. In React.js we always create reusable components so keep the code clean and easy to test. To be able to use DatePicker in your code, create a separate file, write your code for Datepicker and render it as a component of the Field. here is how you should implement.

//First let's start with structure of Formik.
import PortDate from "./form/PortDate";
//define your components in a different directory. I named it form
const CreateForm = props => (
    <div>
      <Formik
        initialValues={{"your initial values"}}
        validate={your validation function here}
        onSubmit={values => {
          return props.onSubmit(values);
        }}
      >
        {({ isSubmitting }) => (
          <Form>     
            <Field name="startDate" component={DatePicker} label="Start Date" />

            <Field
              name="endDate"
              component={DatePicker}
              label="End Date"
              canBeDisabled={true}
            />

          </Form>
        )}
      </Formik>
    </div>
  );

now in a different component let's implement our Datepicker logic. //Use reactstrap instead of bootstrap when you want to add style

import { FormGroup, Label } from "reactstrap";
class DatePicker extends React.Component {
    constructor(props) {
      super(props);
      this.state = {
        dueDate: new Date(),
      };
      this.handleChange = this.handleChange.bind(this);
    }
    setFieldValueAndTouched(date, touched) {
      const { setFieldValue, setFieldTouched } = this.props.form;
      const { name } = this.props.field;
      setFieldValue(name, date, true); //field,value,shouldValidate
      setFieldTouched(name, touched, true); //field,touched,shouldValidate
    }
    handleChange(date) {

      this.setState(() => ({ dateValue: date }));
      this.setFieldValueAndTouched(date, true);    }

    render() {
      const { dueDate } = this.state;
      const {
        label,
        field,
        form: { touched, errors },
      } = this.props;
      // field and form props are passed to this component automatically because we render this inside component of the Field. 
      return (
        <FormGroup>
          <Label>{label}</Label>
          <div className="input-group">

              <DatePicker

                selected={dueDate}
                onChange={this.handleChange}
                peekNextMonth
                showMonthDropdown
                showYearDropdown
                maxDate={new Date()}
                dropdownMode="select"
              />

          </div>
        </FormGroup>
      );
    }
  }

  export default DatePicker;

1 Comment

The question is about using an existing DatePicker class, defined in an imported module. There isn't any code in the question to move to a different file; its simply a form containing two <DatePicker> elements. Sorry, I think your answer is not relevant to this question.
1

How to make this to work for the date range picker:

const [startDate, setStartDate] = useState(new Date());
const [endDate, setEndDate] = useState(new Date());
const { setFieldValue } = useFormikContext();
const [field] = useField({ name: name, value: startDate });
const [field2] = useField({ name: name2, value: endDate });

<GroupContainer>
 <DatePicker
    {...field}
    onFocus={() => setFocusStart(true)}
    onCalendarClose={() => setFocusStart(false)}
    selected={(field.value && new Date(field.value)) || null}
    onChange={(val) => {
                    setStartDate(val);
                    setFieldValue(field.name, val);
                }}
    dateFormat="dd.MM.yyyy" 
    selectsStart                            
    minDate={new Date()}                                
            />
</GroupContainer>

<GroupContainer>
  <DatePicker
    {...field2}
    onFocus={() => setFocusEnd(true)}
    onCalendarClose={() => setFocusEnd(false)}          
    selected={(field2.value && new Date(field2.value)) || null}
    onChange={(val) => {
            setEndDate(val);
            setFieldValue(field2.name, val);
                }}
    dateFormat="dd.MM.yyyy"         
    selectsEnd
    minDate={new Date()}        
            />
</GroupContainer>

Then:

<ReactDatePicker name="arrivalDate" name2="departureDate" />

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.