React 中表单的方法论
动机
最近一年都在做 B 端的项目,由于存在着大量的表单。所以会很容易会发现在实现表单功能上,实现的方法根据不同的人会有很大的不同。
核心法则
单一数据源
首先要明确一点,要存在一个单一数据源 store, store中的状态与组件的视图是一一对应的
,这样保证了数据的流动是单向的,代码在更易读的同时也更便于之后的维护,如下图所示。
Good Example
而我们没有单一数据源的话,只是将状态交由给我们的表单组件维护,很容易出现父与子,兄与弟,孙与爷之间的数据传输,造成代码的腐败,甚至在一段时间之后自己都难以理清其中的逻辑。如下图所示。
Bad Example
受控组件
即自身内不存在状态(例
useState
,useContext
,useReducer
等等...)的组件
我们应该将所有的表单组件设计为受控组件
,表单只需要关心组件状态的输入(value)及输出(onChange),这样一来我们不仅可以通过其他组件更新组件的状态,也可以很容易地将状态分享到其他的 UI 组件当中
// good
function Input({ value, onChange }) {
return (
<input
value={value}
onChange={function handleChange(ev) {
onChange(ev.target.value)
}}
/>
)
}
// bad
function Input({ value, onChange }) {
const [state, setState] = useState(value)
return (
<input
value={state}
onChange={function handleChange(ev) {
const v = ev.target.value
setState(v)
onChange(v)
}}
/>
)
}
用户体验
初始化完成后渲染表单
在业务中我们经常会碰到这样的场景,例如有一个修改当前用户信息的表单,我们需要先通过 http 请求来向服务器获取当前用户的信息来作为表单的默认值。往往我们都是先渲染表单,等到数据加载完成之后,再次将表单重新渲染。很可能用户还在填写表单时,啪,你把表单重新渲染了,但是之前用户填写就丢失了。这就造成了用户的体验感变得很糟糕 😣
所以应当在表单初始化完成后渲染表单。