Search code examples
javascriptreactjsdatepickerreact-datepicker

Update state with Reactjs DatePicker doesn't work


I add projects with datepicker and it works but when I try to edit/update those projects with datepicker, doesn't work! give me many error such as:

  • Failed prop type: Invalid prop selected of type string supplied to DatePicker, expected object

  • A component is changing an uncontrolled input of type text to be controlled. Input elements should not switch from uncontrolled to controlled (or vice versa). Decide between using a controlled or uncontrolled input element for the lifetime of the component.

  • Failed prop type: Invalid prop selected of type string supplied to Calendar/Month/Week/Day, expected object.

  • Uncaught TypeError: Cannot read property 'value' of undefined

and the input is empty, doesn't show today date like it was supposed to, example here: enter image description here

Here's my EditProject Component:

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

import './EditProject.css';
import 'react-datepicker/dist/react-datepicker.css';


class EditProject extends Component {
    constructor(props) {
        super(props);
        this.state = {
            project: {}
        }    
    }

    componentDidMount() {

        // console.log('PROPS ' + JSON.stringify(this.props));

        const { match: { params } } = this.props;


        fetch(`/dashboard/project/${params.id}/edit`)
            .then(response => {
                return response.json()
            }).then(project => {
                this.setState({
                    projectname: project.projectname,
                    typeofproject: project.typeofproject,
                    imageURL: project.imageURL,
                    startDate: project.startDate,
                    endDate: project.endDate
                })
            })
    }

    render() {
        const { match: { params } } = this.props;

        return (
            <div className='EditProject'>
                <h1 className='EditProject__title'>Edit</h1><h1 className='EditProject__projectname'>{this.state.projectname}</h1>
                <hr />
                <form method='POST' action={`/dashboard/project/${params.id}/edit?_method=PUT`}>
                    <div className='form-group container'>
                        <div className="input-group mb-3">
                            <div className="input-group-prepend">
                                <span className="input-group-text" style={{border:'none'}} id="basic-addon1">Project Name</span>
                            </div>
                            <input type='text' className='form-control input' placeholder='insert new project name' value={this.state.projectname} name='projectname' ref='projectname' onChange={(event) => this.setState({ projectname: event.target.value })} />
                        </div>
                    </div>
                    <div className='form-group container'>
                        <div className="input-group mb-3">
                            <div className="input-group-prepend">
                                <span className="input-group-text" style={{border:'none'}} id="basic-addon1">Type of Production</span>
                            </div>
                            <select className='form-control input' value={this.state.typeofproject} name='typeofproject' ref='typeofproject' onChange={(event) => this.setState({ typeofproject: event.target.value })}>
                                <option value='Film (Cinema)'>Film (Cinema)</option>
                                <option value='Film (TV)'>Film (TV)</option>
                                <option value='Film (series)'>Film (series)</option>
                                <option value='Short film'>Short film</option>
                                <option value='TV mini-series'>TV mini-series</option>
                                <option value='TV series'>TV series</option>
                                <option value='TV program'>TV program</option>
                                <option value='TV reporting'>TV reporting</option>
                                <option value='Vox Pop'>Vox Pop</option>
                                <option value='Advertisement Ad'>Advertisement Ad</option>
                                <option value='Documentary'>Documentary</option>
                                <option value='Documentary (TV)'>Documentary (TV)</option>
                                <option value='Documentary (series)'>Documentary (series)</option>
                                <option value='Commercial'>Commercial</option>
                                <option value='Video Clip'>Video Clip</option>
                                <option value='Live Video Clip TV'>Live Video Clip TV</option>
                                <option value='Photography Session'>Photography Session</option>
                            </select>
                        </div>
                    </div>
                    <div className='form-group container'>
                    <div className="input-group mb-3">
                        <div className="input-group-prepend">
                            <span className="input-group-text" style={{border:'none'}} id="basic-addon1">Start Date</span>
                        </div>
   // ================DATEPICKER HERE================
                        <DatePicker 
                            todayButton={"Today"}
                            dateFormat="DD/MM/YYYY"
                            selected={this.state.startDate}
                            value={this.state.startDate}
                            onChange={(event) => this.setState({ startDate: event.target.value })}
                        />
                    </div>
                </div>
                <div className='form-group container'>
                    <div className="input-group mb-3">
                        <div className="input-group-prepend">
                            <span className="input-group-text" style={{border:'none'}} id="basic-addon1">End Date</span>
                        </div>
                        <DatePicker
                            todayButton={"Today"}
                            dateFormat="DD/MM/YYYY"
                            selected={this.state.endDate}
                            value={this.state.endDate} 
                            onChange={(event) => this.setState({ endDate: event.target.value })}
                        />
                    </div>
                </div>
   // / ================DATEPICKER HERE================
                    <div className='form-group container'>
                        <div className="input-group mb-3">
                            <div className="input-group-prepend">
                                <span className="input-group-text" style={{border:'none'}} id="basic-addon1">Project Image URL</span>
                            </div>
                            <input type='text' className='form-control input 'placeholder='insert new project image URL' value={this.state.imageURL} name='imageURL' ref='imageURL' onChange={(event) => this.setState({ imageURL: event.target.value })} />
                        </div>
                    </div>                  
                    <div className='form-group container'>
                        <button type='submit' className='btn btn-default button--update'>Update</button>
                        <NavLink to={`/project/${params.id}/`} ><button type='submit' style={{ backgroundColor: '#b5b5b5' }} className='btn btn-default button--update'>Cancel</button></NavLink>
                    </div>
                </form>
            </div>
        );
    }
} 
export default EditProject;

Thank you so much for your help!


Solution

  • The dates coming from your fetch need to be converted to a Moment date. I do not know what format your date is being returned in but if its not in an ISO 8601 format (YYYY-MM-DD) then you'll also need to specify the format when creating the Moment object so change your code to one of the following:

    startDate: moment(project.startDate)
    startDate: moment(project.startDate, 'DD-MM-YYYY') //specify format that is relevant for your date string if it is not ISO8601
    

    The onChange event handler that is provided by the component returns the selected date (it is not like a normal JS event handler). Therefore you will not receive an object that has event.target.value. Instead you will receive the date selected so just set the value of the param to the startDate in your state object:

     onChange={(newDate) => this.setState({ startDate: newDate })}
    

    Note the value will already be a moment date so doesn't need converting.

    You will then need to do likewise with the endDate and the endDate datepicker.

    Also note that it is not really necessary to set the value property unless you need to change what is actually displayed in the input element - usually you can achieve the correct output using just the selected and dateFormat property (and certainly setting both value and selected to the same thing will result in an error as the former requires a string and the latter a moment object).

    The error regarding controlled to uncontrolled is probably caused by you not initialising your state fully in your constructor - you should initialise all of your components props to something sensible in the constructor as the response to the fetch will almost certainly occur after the initial render as its promise based. Your initial state in your constructor should look something like this (the project property seems unnecessary by the way):

    this.state = {
            projectname: '',
            typeofproject: 'Film (Cinema)', //if you want a default selected otherwise leave as empty string
            imageURL: '',
            startDate: moment(),
            endDate: moment()
        }