48

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.

Yilmaz
  • 35,338
  • 10
  • 157
  • 202
Mel
  • 2,481
  • 26
  • 113
  • 273
  • Never mind everyone. Ive given up trying to figure this out. I found react-formik-ui an it works just fine – Mel May 26 '19 at 10:22
  • 6
    Still answering because this question is valid and may be useful for someone with same query in future. – Dani Vijay May 28 '19 at 13:44

6 Answers6

73

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

SushiWaUmai
  • 348
  • 6
  • 20
ToolmakerSteve
  • 18,547
  • 14
  • 94
  • 196
  • 6
    This is the best answer now (as of end of 2019) – Farax Dec 12 '19 at 05:57
  • 3
    There is also a direct `setValue` helper. `const [field,, { setValue }] = useField(props);` and then `onChange={val => setValue(val)}` – asologor Jan 24 '20 at 12:23
  • @ToolmakerSteve on your demo, as soon as I click in the field I get `RangeError Use 'yyyy' instead of 'YYYY' for formatting years; see: https://git.io/fxCyr` – Wodin Mar 16 '20 at 05:24
  • Looks like a problem with the demo. I don't get that problem if I copy'n'paste your DatePicker component locally. I get other errors when selecting a date, but I think that's unrelated to your code. Thanks. – Wodin Mar 16 '20 at 05:44
  • @Wodin - thanks - fixed. (I had to update `react-datepicker` version to one that matches the latest version of `react`. The message was from a default date format string inside react-datepicker.) – ToolmakerSteve Mar 30 '20 at 18:45
  • Do you guys know how to answer my question that arises from this ? https://stackoverflow.com/questions/61312273/typescript-error-and-the-spread-operator-arising-from-props – nuxer Apr 19 '20 at 22:26
  • This solution also works with Material UI Pickers if anyone else is wondering – Gzim Helshani May 02 '20 at 10:08
  • How about react-native, can you use `useField` with react-native? – eugene May 26 '20 at 13:34
  • @eugene - while I have not tested this with React Native, it should work (assuming you are using Formik 2 or higher). – ToolmakerSteve Jan 13 '21 at 23:45
  • There's a problem with this: Formik doesn't put `is-invalid` on this control when it fails validation. – gene b. May 09 '21 at 14:37
  • @geneb. - hmm. Its been awhile since I wrote this. I don't remember whether I tested to see if it was working back then. So I'm not sure if that is due to some recent change, or there is some problem with this technique. Were you able to find a solution? If you were, go ahead and edit this answer. OR if some different technique works, please link to that. OR create a new question, and put a link to that question here. OR if you solved it yourself, would be great if you create a question, and add your own answer to it. Either way, Thanks! – ToolmakerSteve May 09 '21 at 20:24
  • 1
    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. – gene b. May 09 '21 at 20:58
  • 1
    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: https://stackoverflow.com/questions/67662804/react-datepicker-with-formik-and-yup-date-value-not-validated-on-first-blur-ot – gene b. May 23 '21 at 17:53
  • 2
    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. – nycynik Jul 22 '21 at 22:29
  • I am having error `Cannot read properties of undefined (reading 'setFieldValue')` when try to destructure `useFormikContext()` hook – Mekel Ilyasa Jul 19 '22 at 18:05
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

Dani Vijay
  • 2,188
  • 2
  • 22
  • 37
  • 1
    I don't know why I had to do that : onChange={(val) => setFieldValue('date', val)} – Damien Romito Oct 18 '19 at 16:20
  • 1
    @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'`. – ToolmakerSteve Oct 31 '19 at 18:59
  • @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](https://codesandbox.io/s/formik-react-datepicker-demo-1zz6e). Usage: ``. [my answer](https://stackoverflow.com/a/58650742/199364). – ToolmakerSteve Oct 31 '19 at 20:23
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.

Logicism
  • 51
  • 4
  • This answer was useful for working with NextJs, being used as ``````. 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. – w. Patrick Gale Feb 07 '22 at 18:20
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;
Yilmaz
  • 35,338
  • 10
  • 157
  • 202
  • 1
    The question is about using [an existing DatePicker class, defined in an imported module](https://github.com/Hacker0x01/react-datepicker/blob/master/src/index.jsx). There isn't any code in the question to move to a different file; its simply a form containing two `` elements. Sorry, I think your answer is not relevant to this question. – ToolmakerSteve Oct 31 '19 at 18:23
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" />
sFritsch09
  • 424
  • 4
  • 10
1
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>
Dharman
  • 30,962
  • 25
  • 85
  • 135