I am looking to create a form with a datepicker in my React component with materialize-css. I don't have many fields this form is capturing and the structure is fairly simple. The form returned looks like this:
<form onSubmit={this.handleSubmit.bind(this)}>
<div className="container">
<div className="card grey lighten-3">
<div className="card-content black-text">
<span className="card-title">
<input placeholder="Event Name"
name="name" value={this.state.name}
onChange={this.handleStateChange.bind(this)}/>
</span>
<input name="description" placeholder="Description"
value={this.state.description}
onChange={this.handleStateChange.bind(this)}/>
<input name="image" placeholder="Image URL"
value={this.state.image}
onChange={this.handleStateChange.bind(this)}/>
<input placeholder="Start Date"
className="datepicker" name="startDate" value={this.state.startDate}
onSelect={this.handleStateChange.bind(this)}/>
</div>
<div class="card-action">
<div className="row">
<span>
<div className="col s3">
<input className="btn light-blue accent-1" type="submit" value="Submit"/>
</div>
<div className="col s3">
<a className="btn grey" onClick={this.handleExpand.bind(this)}>Cancel</a>
</div>
</span>
</div>
</div>
</div>
</div>
</form>
The state change is handled with
handleStateChange(item) {
this.setState({[item.target.name]: item.target.value});
}
and I have called the AutoInit to initialize my datepicker
M.AutoInit();
I've tried using onChange
instead of onSelect
to manage the datepicker state change, but it doesn't seem to capture that event. With onSelect
used, the date sometimes gets capture if I pick a date then re-open the datepicker.
I have also tried using some of the alternate initialization methods for the datepicker to no avail.
How do I correctly capture the input change with my given setup?
After studing all the saturday about the lifecycle of React. I got this solution for this:
The use of the component:
<DatePicker label="Fecha" value={this.state.formSchedule.start}
onChange={(date) => {
this.state.formSchedule.start = date;
this.setState({ formSchedule: this.state.formSchedule });
}}/>
The Class DatePicker.tsx:
import * as React from 'react';
import Materialize from 'materialize-css';
import moment from 'moment'
import 'moment/locale/es'
import { any } from 'prop-types';
interface IState {
value: any;
}
interface IProps {
label: any;
format: any;
onChange: any;
formatMoment: any;
}
export default class DatePicker extends React.Component<IProps, IState> {
static defaultProps = {
label: "Fecha",
value: new Date(),
format: 'ddd d, mmm',
formatMoment: 'ddd D, MMM'
}
constructor(props: any) {
super(props);
this.componentWillReceiveProps(props);
}
componentWillReceiveProps(props) {
this.state = {
value: props.value
};
}
render() {
return <div className="input-field col s6">
<i className="material-icons prefix">date_range</i>
<input id="date" type="text" className="datepicker queso"
value={moment(this.state.value).locale('es').format(this.props.formatMoment)}
/>
<label className="active" htmlFor="date">{this.props.label}</label>
</div>;
}
componentDidMount() {
var context = this;
var elems = document.querySelectorAll('.queso');
Materialize.Datepicker.init(elems, {
defaultDate: new Date(),
format: this.props.format,
container: 'body',
onSelect: function (date) {
context.setState({ value: context.state.value });
context.props.onChange(date);
},
autoClose: true
} as Partial<any>);
}
}
Hi hopefully this will help somebody -
What happens with the <DatePicker /> component is that the default onChange method returns the date (2019-08-01) and not the the element's event handler object. In order to counter this we need to create an object inside the onChange method that mimics the eventhandler's target.id and target.value
Other components like the <input /> works as per normal. Check it out:
This is what the component should look like:
<DatePicker
label="myDate"
value={state.myDate}
id="myDate"
onChange={(newDate) => {
handleChange({
target: {
id: "myDate",
value: newDate
}
})
}} />
Here is the full code:
import React, { useState, useEffect } from "react";
import "materialize-css/dist/css/materialize.min.css";
import "materialize-css/dist/js/materialize.min.js";
import { DatePicker } from "react-materialize";
const PickDate = (props) => {
const [state, setState] = useState({myName: "Mags", myDate: "2019-08-01"});
const handleChange = (e) => {
const key = e.target.id;
const val = e.target.value;
const newState = {...state};
newState[key] = val;
setState(newState);
}
return (
<React.Fragment>
<input type="text" value={state.myName} id="myName" onChange={handleChange} />
<DatePicker
label="myDate"
value={state.myDate}
id="myDate"
onChange={(newDate) => {
handleChange({
target: {
id: "myDate",
value: newDate
}
})
}} />
</React.Fragment>
);
}
export default PickDate;
PS. this uses React Hooks - but it will work on normal classes too.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With