diff --git a/src/common/auto-controlled-input.js b/src/common/auto-controlled-input.js
new file mode 100644
index 000000000..9423db49b
--- /dev/null
+++ b/src/common/auto-controlled-input.js
@@ -0,0 +1,86 @@
+import React from 'react'
+import { omit } from 'lodash'
+
+import Component from './base-component'
+import getEventValue from './get-event-value'
+
+const __DEV__ = process.env.NODE_ENV !== 'production'
+
+// This decorator can be used on a controlled input component to make
+// it able to automatically handled the uncontrolled mode.
+export default () => ControlledInput => {
+ class AutoControlledInput extends Component {
+ constructor (props) {
+ super()
+
+ const controlled = this._controlled = props.value !== undefined
+ if (!controlled) {
+ this.state.value = props.defaultValue
+
+ this._onChange = event => {
+ let defaultPrevented = false
+
+ const { onChange } = this.props
+ if (onChange) {
+ onChange(event)
+ defaultPrevented = event && event.defaultPrevented
+ }
+
+ if (!defaultPrevented) {
+ this.setState({ value: getEventValue(event) })
+ }
+ }
+ } else if (__DEV__ && props.defaultValue !== undefined) {
+ throw new Error(`${this.constructor.name}: uncontrolled component should not have a default value`)
+ }
+ }
+
+ get value () {
+ return this._controlled
+ ? this.props.value
+ : this.state.value
+ }
+
+ set value (value) {
+ if (__DEV__ && this._controlled) {
+ throw new Error(`${this.constructor.name}: should not set value on controlled component`)
+ }
+
+ this.setState({ value })
+ }
+
+ render () {
+ if (this._controlled) {
+ return
+ }
+
+ return
+ }
+ }
+
+ if (__DEV__) {
+ AutoControlledInput.prototype.componentWillReceiveProps = function (newProps) {
+ const { name } = this.constructor
+ const controlled = this._controlled
+ const newControlled = newProps.value !== undefined
+
+ if (!controlled) {
+ if (newControlled) {
+ throw new Error(`${name}: uncontrolled component should not become controlled`)
+ }
+ } else if (!newControlled) {
+ throw new Error(`${name}: controlled component should not become uncontrolled`)
+ }
+
+ if (newProps.defaultValue !== this.props.defaultValue) {
+ throw new Error(`${name}: default value should not change`)
+ }
+ }
+ }
+
+ return AutoControlledInput
+}