Skip to content

Commit 32a7146

Browse files
committed
Fix bug in DateInput where props.value differs from internal state
1 parent bfae9b3 commit 32a7146

File tree

2 files changed

+50
-9
lines changed

2 files changed

+50
-9
lines changed

src/components/date-input/DateInput.tsx

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ type DateInputChangeEvent = ChangeEvent<HTMLInputElement> & {
1717

1818
interface DateInputProps
1919
extends Omit<HTMLProps<HTMLDivElement>, 'value' | 'defaultValue'>,
20-
FormElementProps {
20+
FormElementProps {
2121
autoSelectNext?: boolean;
2222
value?: Partial<DateInputValue>;
2323
defaultValue?: Partial<DateInputValue>;
@@ -38,16 +38,47 @@ interface DateInput extends PureComponent<DateInputProps, DateInputState> {
3838
}
3939

4040
class DateInput extends PureComponent<DateInputProps, DateInputState> {
41+
static Day = DayInput;
42+
43+
static Month = MonthInput;
44+
45+
static Year = YearInput;
46+
4147
constructor(props: DateInputProps, ...rest: any[]) {
4248
super(props, ...rest);
4349
this.state = {
44-
values: { day: '', month: '', year: '' },
50+
values: {
51+
day: props.value?.day || '',
52+
month: props.value?.month || '',
53+
year: props.value?.year || '',
54+
},
4555
};
4656

4757
this.monthRef = null;
4858
this.yearRef = null;
4959
}
5060

61+
componentDidUpdate(prevProps: DateInputProps) {
62+
if (this.props.value && prevProps.value !== this.props.value) {
63+
// This is the only way that we can update our internal state
64+
// when the value updates. We check if the value has changed first,
65+
// preventing an infinite loop.
66+
//
67+
// eslint-disable-next-line react/no-did-update-set-state
68+
this.setState(state => {
69+
if (!this.props.value) return state;
70+
71+
const newState = { ...state };
72+
const { day, month, year } = this.props.value;
73+
if (day && day !== state.values.day) newState.values.day = day;
74+
if (month && month !== state.values.month) newState.values.month = month;
75+
if (year && year !== state.values.year) newState.values.year = year;
76+
77+
return newState;
78+
});
79+
}
80+
}
81+
5182
handleSelectNext = (inputType: 'day' | 'month' | 'year', value: string) => {
5283
if (!this.props.autoSelectNext) return;
5384
if (inputType === 'day' && value.length === 2 && this.monthRef) {
@@ -82,11 +113,6 @@ class DateInput extends PureComponent<DateInputProps, DateInputState> {
82113
if (inputType === 'year') this.yearRef = ref;
83114
};
84115

85-
static Day = DayInput;
86-
87-
static Month = MonthInput;
88-
89-
static Year = YearInput;
90116

91117
render() {
92118
const { children, onChange, value, defaultValue, ...rest } = this.props;

stories/FormElementBehaviour/DateInput.stories.tsx

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/* eslint-disable import/no-extraneous-dependencies */
2-
import React from 'react';
2+
import React, { useState } from 'react';
33
import { storiesOf } from '@storybook/react';
4-
import { DateInput, Form } from '../../src';
4+
import { DateInput } from '../../src';
55

66
const stories = storiesOf('FormElementBehaviour: DateInput', module);
77

@@ -90,4 +90,19 @@ stories
9090
<DateInput hint="Test hint" label="Test label" value={value} />
9191
</div>
9292
);
93+
})
94+
.add('Changeable Controlled Element', () => {
95+
const [value, setValue] = useState({ day: '20', month: '09', year: '1996' });
96+
97+
return (
98+
<div style={{ padding: 20 }}>
99+
<h5>Component</h5>
100+
<DateInput
101+
hint="Test hint"
102+
label="Test label"
103+
value={value}
104+
onChange={e => setValue(e.currentTarget.value)}
105+
/>
106+
</div>
107+
);
93108
});

0 commit comments

Comments
 (0)