I'm using react-dates in an advanced search component that incorporates multiple date-range fields.
I'm trying to figure out the best way to detect which of the date-range pickers is being changed and handle the event accordingly in the onFocusChange and onDateChange callbacks without writing a separate handler with superficial differences to target the appropriate attribute for each instance of the component.
Is there a way to access the event target within the defined callbacks?
I'm imagining something along the lines of:
onFocusChange(event, focusedInput){
var newState = object.assign({}, this.state);
newState[event.target.id].focusedInput = focusedInput;
this.setState(newState);
}
But maybe it would be a better approach to store another variable on the state which stores the focused date-range picker?
Any insight would be greatly appreciated!
Update: I've written a closure to return a case-specific handler for each field which is adequate to my use case, but I am still curious if it's possible to access the source event.
Hmmm, maybe we should pass it back just in case. I will mull this over.
I'm in the same boat here. I am managing a few massive forms, and all other inputs update the parent state in much the same way that @andosteinmetz has described:
My thoughts were that react-dates would behave much like any other input element. Of course the moment object needs to be passed to the callback, but it seems counter-intuitive to not have the event passed like any other input.
Did you find a temporary workaround?
Hi @tyleralves ,
My solution was to write a closure that returns a function specific to the active react dates instance.
In my case there was one instance to set a range for when a record was created, and another range for the date the record was most recently modified: called 'created' and 'changed'.
Here are my two handler functions for focus and date changes respectively:
function handleFocusChange(attr){
return function(focusedInput){
var dates = this.state[attr];
dates.focusedInput = focusedInput;
let newState = {};
newState[attr] = dates;
this.setState(newState);
}
}
function handleDatesChange(attr){
return function({startDate, endDate}){
var dates = this.state[attr];
dates.startDate = startDate;
dates.endDate = endDate;
startDate ? dates.gte = startDate.format('YYYY[-]MM[-]DD') : delete dates.gte;
endDate ? dates.lte = endDate.format('YYYY[-]MM[-]DD') : delete dates.lte;
let newState = {};
newState[attr] = dates;
this.setState(newState);
}
}
and here's how I used it in my component:
class MyComponent extends React.Component
{
constructor(props){
super(props);
this._onCreatedFocusChange = handleFocusChange('created').bind(this);
this._onCreatedDatesChange = handleDatesChange('created').bind(this);
this._onUpdatedFocusChange = handleFocusChange('changed').bind(this);
this._onUpdatedDatesChange = handleDatesChange('changed').bind(this);
}
and then in the ReactDates instances:
render(){
const createdRange = (
<div className="date-range">
<DateRangePickerField
isOutsideRange = {() => false }
showClearDates = {true}
label = 'Created:'
focusedInput = {this.state.created.focusedInput}
startDate = {this.state.created.startDate}
endDate = {this.state.created.endDate}
onDatesChange = {this._onCreatedDatesChange}
onFocusChange = {this._onCreatedFocusChange}
/>
</div>
);
const changedRange = (
<div className="date-range">
<DateRangePickerField
isOutsideRange = {() => false}
showClearDates = {true}
label = 'Updated:'
focusedInput = {this.state.changed.focusedInput}
startDate = {this.state.changed.startDate}
endDate = {this.state.changed.endDate}
onDatesChange = {this._onUpdatedDatesChange}
onFocusChange = {this._onUpdatedFocusChange}
/>
</div>
);
Hope this is helpful in your case as well!
@andosteinmetz Hmm, we have a different setup but your solution got me to where I needed to go. Thanks!
My situation is a container component (EditPublicationContainer) that maintains the full form state and has many child input components (FormInput). An onUpdateField function on the EditPublicationContainer component updates state. The onUpdateField may not be bound to the child component's this because it still must call this.setState in the parent component.
/* EditPublicationContainer Component */
handleUpdateField = (value: any, self?: any) => {
// Update local state (temp form data) when input value changes
// React-datepicker doesn't return an event object, so component props are passed through self
const property = self.props.property;
this.setState({
publication: {
...this.state.publication,
[property]: value
}
});
}
/* FormInput Component */
export class FormInput extends React.Component<FormInputProps, any> {
constructor() {
super();
}
// React-datepicker doesn't return a consistent event object, so we pass this directly instead
handleUpdateDateField = (date: any) => this.props.onUpdateField(date, this);
render() {
const {props} = this;
let inputElement;
switch (props.type) {
case 'date':
// tslint:disable-next-line max-line-length
// tslint:disable
inputElement = (
<DatePicker
data-type={props.type}
id={`form-input-${props.type}-${props.property}`}
name={props.property}
selected={props.value || moment()}
onChange={this.handleUpdateDateField} // Call the class property
{...props.xtra}/>
);
break;
...
I am using multiple DateRangePicker that dynamically added in single page. There is another solution.
constructor(props) {
super(props);
this.onDatesChange = this.onDatesChange.bind(this);
this.onFocusChange = this.onFocusChange.bind(this);
this.state = {
startDate: {},
endDate: {},
focusedInput: {},
};
}
// id is unique value for each instance and you can define it
onDatesChange(id, { startDate, endDate }) {
const start = this.state.startDate;
const end = this.state.endDate;
start[id] = startDate;
end[id] = endDate;
this.setState({
startDate: start,
endDate: end,
});
}
onFocusChange(id, value) {
const focusedInput = this.state.focusedInput;
focusedInput[id] = value;
this.setState({
focusedInput,
});
}
<DateRangePicker
startDate={this.state.startDate[id]}
endDate={this.state.endDate[id]}
onDatesChange={({ startDate, endDate }) => this.onDatesChange(id, { startDate, endDate })}
focusedInput={this.state.focusedInput[id]}
onFocusChange={(focusedInput) => this.onFocusChange(id, focusedInput)}
/>
@munkhjin0223 if the startDate is null, typed error occurred
Most helpful comment
I am using multiple DateRangePicker that dynamically added in single page. There is another solution.