gpbl / react-day-picker

DayPicker is a customizable date picker component for React. Add date pickers, calendars, and date inputs to your web applications.
https://daypicker.dev
MIT License
6.01k stars 720 forks source link

Cannot Clear Dates on DayPickerInput in latest version (7.4.0^) #977

Closed nickavignone closed 4 years ago

nickavignone commented 4 years ago

Trying to set dates to undefined to clear, causes the below error A component is changing a controlled input of type undefined to be uncontrolled. Input elements should not switch from controlled to uncontrolled (or vice versa). Decide between using a controlled or uncontrolled input element for the lifetime of the component.


import * as React from 'react';
import moment from 'moment';
import { formatDate, parseDate } from 'react-day-picker/moment';
import DayPickerInput from 'react-day-picker/DayPickerInput';
class DayPickerTest extends React.Component<Props> {

  constructor(props) {
    super(props);
    this.handleFromChange = this.handleFromChange.bind(this);
    this.handleToChange = this.handleToChange.bind(this);
    this.state = {
      from: undefined,
      to: undefined,
    };
  }

  showFromMonth() {
    const { from, to } = this.state;
    if (!from) {
      return;
    }
    if (moment(to).diff(moment(from), 'months') < 2) {
      this.to.getDayPicker().showMonth(from);
    }
  }

  handleFromChange(from) {
    // Change the from date and focus the "to" input field
    this.setState({ from });
  }

  handleToChange(to) {
    this.setState({ to }, this.showFromMonth);
  }

  clearDates = () => {
    this.setState({
      from: undefined,
      to: undefined,
    });
  }

  render() {
    const { from, to } = this.state;
    const modifiers = { start: from, end: to };
    return (
      <div className="InputFromTo">
        <div className="test"
          onClick={this.clearDates}
        />
        <DayPickerInput
          value={from}
          placeholder="From"
          format="LL"
          formatDate={formatDate}
          parseDate={parseDate}
          dayPickerProps={{
            selectedDays: [from, { from, to }],
            disabledDays: { after: to },
            toMonth: to,
            modifiers,
            numberOfMonths: 2,
            onDayClick: () => this.to.getInput().focus(),
          }}
          onDayChange={this.handleFromChange}
        />{' '}
        —{' '}
        <span className="InputFromTo-to">
          <DayPickerInput
            ref={el => (this.to = el)}
            value={to}
            placeholder="To"
            format="LL"
            formatDate={formatDate}
            parseDate={parseDate}
            dayPickerProps={{
              selectedDays: [from, { from, to }],
              disabledDays: { before: from },
              modifiers,
              month: from,
              fromMonth: from,
              numberOfMonths: 2,
            }}
            onDayChange={this.handleToChange}
          />
        </span>
      </div>
    );
  }
}

export default DayPickerTest;
`
marcus-kindred commented 4 years ago

A work around people can use if they need to:

Pass a custom input component to DayPickerInput with the component property and have that input replace undefined with an empty string.

henryruhs commented 4 years ago

Works for me with 7.4.8 ... thanks for the fix

himat commented 4 years ago

General react question: does it matter whether we use null or undefined to reset the component?

henryruhs commented 4 years ago

@himat I recommend to use null everywhere and ban undefined from your code. This approach comes from a talk by Douglas Crockford who pointed out that we don't need two types of "not set" and should stick to one of them.

rstrand commented 4 years ago

@redaxmedia Interestingly Douglas Crockford himself stopped using null and only uses undefined with the rationale that undefined is already used a lot internally in javascript.

nick-lvov-dev commented 3 years ago

Isn't it like this: undefined - not set null - set to nothing

I don't think you can really get rid of one or the other, they mean different things. If you get a model from API it's empty fields are set to null, for example.