Search code examples
reactjsdatepickerreact-datepicker

React Datepicker


I'm trying to create small calendar calculator which subtracts two dates and give amount of days.

For that I've chosen library react-datepicker.

Now I have a problem that state updates one click after. This means that when I choose date 2nd time I get result for the previous one.

Also, I can't understand why when I uncomment ...this.state for saving immutability of state it stops updating.

There is my code

import React, { Component } from 'react';
import DatePicker from 'react-datepicker';
import moment from 'moment';

import 'react-datepicker/dist/react-datepicker.css';
import CalendarInput from '../../UI/CalendarInput/CalendarInput';

class CalendarPage extends Component {
  constructor(props) {
    super(props);
    this.state = {
      startDate: moment(),
      endDate: moment(),
      days: 0
    };
    this.handleChangeEnd = this.handleChangeEnd.bind(this);
    this.handleChangeStart = this.handleChangeStart.bind(this);
    this.daysLeft = this.daysLeft.bind(this);
  }

  daysLeft() {
    let {startDate, endDate} = this.state;
    console.log(startDate);
    console.log(endDate);
    let amount = endDate.diff(startDate, 'days');
    this.setState({
      // ...this.state,
      days: amount
    });
  }

  handleChangeStart(date) {
    this.setState({
      // ...this.state,
      startDate: date
    });
    this.daysLeft();
  }

  handleChangeEnd(date) {
    this.setState({
      // ...this.state,
      endDate: date
    });
    this.daysLeft();
  }

  render() {
    return (
      <div>
        <h2 className="pl-2 mb-4">
          Calendar
        </h2>

        <div className="row no-gutters calculator-container">
          <DatePicker
            selected={this.state.startDate}
            selectsStart
            startDate={this.state.startDate}
            endDate={this.state.endDate}
            onChange={this.handleChangeStart}
            customInput={<CalendarInput label="departure"/>}
          />

          <DatePicker
            selected={this.state.endDate}
            selectsEnd
            startDate={this.state.startDate}
            endDate={this.state.endDate}
            onChange={this.handleChangeEnd}
            customInput={<CalendarInput label="arrival"/>}
          />

          <div className="amount">
            {this.state.days}
          </div>
        </div>

      </div>
    );
  }
}

export default CalendarPage;

Solution

  • You are making multiple calls to setState within one cycle, and referencing this.state within the same cycle. setState does not guarantee to update the state object when you call it, but at some later time.

    Therefore, your calls to this.daysLeft() will be accessing the prior state, not the one you expected as a result of the setState call.

    setState has two modes, one to provide an object which is shallow merged at a later time, another a callback function which is called when state can be updated. You may need to use the latter form so that you can guarantee that state changes are done in sequence. If you read the docs, the latter is actually the preferred mode, the object form is for convenience in simplistic code paths, and historically I think has found its way into a lot of tutorials, which leads to confusion.

    React setState docs

    See if this helps and come back if you modify your code and still have problems. Post your amended code if you do.