Skip to content

Commit 62f7695

Browse files
committed
Allow omitting onChange and use as uncontrolled component
1 parent c77c385 commit 62f7695

File tree

4 files changed

+111
-42
lines changed

4 files changed

+111
-42
lines changed

.eslintrc

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
{
2-
"extends": "./node_modules/react-component-template/.eslintrc"
2+
"extends": "./node_modules/react-component-template/.eslintrc",
3+
"rules": {
4+
"no-empty-function": 0
5+
}
36
}

src/Component.js

Lines changed: 52 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,14 @@ import debounce from 'lodash.debounce';
33
import {shouldComponentUpdate} from 'react/lib/ReactComponentWithPureRenderMixin';
44

55

6+
const noop = () => {};
7+
8+
69
export const DebounceInput = React.createClass({
710
propTypes: {
811
element: React.PropTypes.oneOfType([React.PropTypes.string, React.PropTypes.func]),
912
type: React.PropTypes.string,
10-
onChange: React.PropTypes.func.isRequired,
13+
onChange: React.PropTypes.func,
1114
onKeyDown: React.PropTypes.func,
1215
onBlur: React.PropTypes.func,
1316
value: React.PropTypes.oneOfType([
@@ -66,8 +69,8 @@ export const DebounceInput = React.createClass({
6669

6770

6871
createNotifier(debounceTimeout) {
69-
if (debounceTimeout < 0) {
70-
this.notify = () => null;
72+
if (debounceTimeout < 0 || !this.props.hasOwnProperty('onChange')) {
73+
this.notify = noop;
7174
} else if (debounceTimeout === 0) {
7275
this.notify = this.props.onChange;
7376
} else {
@@ -77,6 +80,10 @@ export const DebounceInput = React.createClass({
7780

7881

7982
forceNotify(event) {
83+
if (!this.props.hasOwnProperty('onChange')) {
84+
return;
85+
}
86+
8087
if (this.notify.cancel) {
8188
this.notify.cancel();
8289
}
@@ -95,21 +102,43 @@ export const DebounceInput = React.createClass({
95102
onChange(event) {
96103
event.persist();
97104

98-
const oldValue = this.state.value;
105+
this.oldValue = this.state.value;
106+
this.setState({value: event.target.value}, this.afterSetState);
107+
},
108+
99109

100-
this.setState({value: event.target.value}, () => {
101-
const {value} = this.state;
110+
afterSetState() {
111+
const {value} = this.state;
102112

103-
if (value.length >= this.props.minLength) {
104-
this.notify(event);
105-
return;
106-
}
113+
if (value.length >= this.props.minLength) {
114+
this.notify(event);
115+
return;
116+
}
107117

108-
// If user hits backspace and goes below minLength consider it cleaning the value
109-
if (oldValue.length > value.length) {
110-
this.notify({...event, target: {...event.target, value: ''}});
111-
}
112-
});
118+
// If user hits backspace and goes below minLength consider it cleaning the value
119+
if (this.oldValue.length > value.length) {
120+
this.notify({...event, target: {...event.target, value: ''}});
121+
}
122+
},
123+
124+
125+
onKeyDown(event) {
126+
if (event.key === 'Enter') {
127+
this.forceNotify(event);
128+
}
129+
// Invoke original onKeyDown if present
130+
if (this.props.hasOwnProperty('onKeyDown')) {
131+
this.props.onKeyDown(event);
132+
}
133+
},
134+
135+
136+
onBlur(event) {
137+
this.forceNotify(event);
138+
// Invoke original onBlur if present
139+
if (this.props.hasOwnProperty('onBlur')) {
140+
this.props.onBlur(event);
141+
}
113142
},
114143

115144

@@ -125,35 +154,17 @@ export const DebounceInput = React.createClass({
125154
...props
126155
} = this.props;
127156

128-
const onKeyDown = forceNotifyByEnter ? {
129-
onKeyDown: event => {
130-
if (event.key === 'Enter') {
131-
this.forceNotify(event);
132-
}
133-
// Invoke original onKeyDown if present
134-
if (this.props.onKeyDown) {
135-
this.props.onKeyDown(event);
136-
}
137-
}
138-
} : {};
139-
140-
const onBlur = forceNotifyOnBlur ? {
141-
onBlur: event => {
142-
this.forceNotify(event);
143-
// Invoke original onBlur if present
144-
if (this.props.onBlur) {
145-
this.props.onBlur(event);
146-
}
147-
}
148-
} : {};
149-
157+
const onChangeProp = this.props.hasOwnProperty('onChange') ? {onChange: this.onChange} : {};
158+
const valueProp = this.props.hasOwnProperty('onChange') ? {value: this.state.value} : {};
159+
const onKeyDownProp = forceNotifyByEnter ? {onKeyDown: this.onKeyDown} : {};
160+
const onBlurProp = forceNotifyOnBlur ? {onBlur: this.onBlur} : {};
150161

151162
return React.createElement(element, {
152163
...props,
153-
onChange: this.onChange,
154-
value: this.state.value,
155-
...onKeyDown,
156-
...onBlur
164+
...valueProp,
165+
...onChangeProp,
166+
...onKeyDownProp,
167+
...onBlurProp
157168
});
158169
}
159170
});

src/example/App/Uncontrolled.js

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import React from 'react';
2+
import ReactDom from 'react-dom';
3+
import DebounceInput from '../..';
4+
import css from './App.css';
5+
6+
7+
const Uncontrolled = React.createClass({
8+
getInitialState() {
9+
return {
10+
value: '',
11+
key: ''
12+
};
13+
},
14+
15+
16+
onRef(ref) {
17+
this.ref = ReactDom.findDOMNode(ref);
18+
},
19+
20+
21+
onKeyDown({key}) {
22+
if (key === 'Enter') {
23+
this.setState({key, value: this.ref.value})
24+
} else {
25+
this.setState({key})
26+
}
27+
},
28+
29+
30+
render() {
31+
const {
32+
value, key
33+
} = this.state;
34+
35+
return (
36+
<div>
37+
<DebounceInput
38+
ref={this.onRef}
39+
onKeyDown={this.onKeyDown} />
40+
<p>Value: {value}</p>
41+
<p>Key pressed: {key}</p>
42+
</div>
43+
44+
);
45+
}
46+
});
47+
48+
49+
export default Uncontrolled;

src/example/App/index.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import Controllable from './Controllable';
33
import Customizable from './Customizable';
44
import UndoRedo from './UndoRedo';
55
import Textarea from './Textarea';
6+
import Uncontrolled from './Uncontrolled';
67
import css from './App.css';
78

89

@@ -28,6 +29,11 @@ const App = () => (
2829
<h2>Example 4. Debounced Textarea</h2>
2930
<Textarea />
3031
</section>
32+
33+
<section className={css.section}>
34+
<h2>Example 5. Uncontrolled input</h2>
35+
<Uncontrolled />
36+
</section>
3137
</div>
3238
);
3339

0 commit comments

Comments
 (0)