antd对于form中输入控件的抽象十分简单,只要能接收value和onChange属性的组件都可以成为Form.Item的子组件,为Form对应的字段提供值。对于输入控件的抽象我认为这已经达到了极致,事件(onChange)产生值(value)。事件向上,值向下,完全符合React哲学。
如何实现一个标准的输入组件?
对于输入组件首先需要具备接收value和onChange属性的能力,这两个属性被提供的情况下这个组件被称为受控组件,它受到父组件控制,状态来自父组件。当父组件没有提供value的情况下该组件是非受控组件,有自己的状态。
是否受控主要取决于value属性是否提供,因为value决定了组件当前的状态,当前状态被父组件控制,所以称之为受控组件。
输入组件的状态是会改变的,最常见的方式就是来自用户行为,例如在input中输入内容,该组件的状态(value)就会改变。
- 因为组件可以作为非受控组件,所以组件自身有innerValue和setInnerValue用于展示和响应用户输入。
- 如果父组件没有提供value
- 组件使用自身的setInnerValue响应用户输入,改变自身状态innerValue
- 如果父组件提供onChange,在setInnerValue同时触发onChange事件通知父组件
- 如果父组件提供了value,组件失去了决定自身状态的能力(自身不会直接响应用户输入)
- 如果父组件提供onChange函数,则使用该函数响应用户输入
- 组件自身的setInnerValue不会响应用户输入(setInnerValue的作用是修改innerValue,因为有父组件的value,innerValue会响应value的变化,所以setInnerValue不应该直接修改innerValue导致其和value不一致)
function Input (props) {const [innerValue, setInnerValue] = useState(props.value);useEffect(() => {if ('value' in props) setInnerValue(props.value)}, [props.value])function handleChange (ev) {if (!('value' in props)) {setInnerValue(ev.target.value)}props.onChange?.(ev.target.value)}return <input value={innerValue} onChange={handleChange} />
}