一 不建议使用 useUncontrolled
组件封装,最好同时支持受控使用和非受控使用(预防未来抽离组件 或者用于循环场景), 一般组件库会使用useUncontrolled, 但是业务代码最好不要
jsx
const [state, setState] = useControllableValue<string>(props, {
const
此类 hook 返回的 setState
支持传入值, 好一些的会支持传入函数,
此类 hook,一般受控非受控的标志是, value === undefined
, 但是 ahooks 不是这样,
但是, 官方 useState const [state,setState] = useState()
返回的 setState 还支持批量更新,也就是传入回调函数,多次调用 setState, 但是这个 hook 是永远无法支持的,至少至今没见过
正确的做法: useState + useEffect
jsx
const [state,setState] = useState(props.value)
useEffect(()=>{
setState(props.value)
},[props.value])
<Child value>
当然, 如果你的应用足够复杂,可以再次封装,参考xyflow
jsx
useStoreUpdater<Node[]>(nodes, setNodes);
当然也可以显式表达非受控,使用 defaultvalue, 参考
jsx
useEffect(() => {
const edgesWithDefaults = defaultEdges?.map((e) => ({ ...e, ...defaultEdgeOptions }));
setDefaultNodesAndEdges(defaultNodes, edgesWithDefaults);
return () => {
reset();
};
}, []);
二 父组件的状态,不要拆散后传给子组件,且尽量不要有多对
- 不要拆散,是因为当数据很复杂时, 书写onValueChange函数时,容易出问题, 这算是一个好习惯吧
- 不要有多对,是因为某些需求下,需要传入回调来支持批量更新, 但是万一你使用了 useUncontrolled,那就得重构,而我们封装组件应该尽量满足各种使用情况
jsx
const [state,setState] = useState()
<Child readonly={state.reanonly} value={state.value} onValueChange={state.value=x}/>
// Child.tsx
useRequest([readonly,value]) // 模拟读取
const onChange = (newVal)=>{ // 模拟修改
onValueChange(newVal)
}
三 Form组件 最好借助框架使用 Form + useForm
jsx
const [state,setState] = useState()
const fetch = (values)=> {
console.log(state) // state 是陈旧的,所以需要通过参数传入,
}
<Form onSubmit={values=> {
setState(values); // 一次更新太多变量,跟容易出 bug,
fetch(values);
setPage(1) // 假如fetch 需要 page, 那么page 也要放到形参里, 这是会无限增加附加度的方式
} } />
最好使用 useForm, 下降复杂度
jsx
const [form] = useForm()
const fetch = ()=> { // 不需要任何参数了
const values = form.getValues()
}
<Form form={form} onSubmit={values=> {
form.setFieldsValue({
...values,
page:1
})
fetch();
} } />
类似于 useStateRef, 另外使用 antd 的Form组件,是因为 onValuesChange是由用户操作才会触发, 而 mantine-form 没有这个 api
jsx
const [form] = useForm() // mantine 没有 onValuesChange, 你只能模拟
// 这段代码跟下面的将造成死循环, 使用 JSON.stringify hack 处理, 在极端场景下也会有问题
useEffect(()=>{
form.setValues(form.values)
},[form.values])
useEffect(()=>{
onChange(form.values)
},[form.values])
<Form form={form} >
四 使用 ahooks 的 useRequest
由于页面不总是查询按钮触发的查询, 或者请求可能在子组件,拿不到 loading,所以无法给按钮正确的 loading 效果, 我们需要保证数据的正确,所以最好使用 ahooks 的 useRequest
五 使用 SWR 来请求下拉列表
在复用下拉列表的场景里, 比如table 里行内编辑, 多个页面共用选择器等, 可以显著增加响应速度 不过注意接口最好是get, 因为类似的 react-query 和 swr, onfetch 推崇 restfulapi, 对 get 支持最好,比如重新校验只支持get, 使用时注意, 黑盒很多,不要过度滥用
六 弹窗编辑
推荐使用 antd 的 Form 来处理,initialValues 里放数据缺失时的默认值,不要放新增按钮的数据, 因为这将被用到合并, finalValue = {...initialValues, ...row}
, 比自己写要轻松一些, 另外setValues 总是增加值, 不支持reset, 所以你的 Modal 最好是 {show? <Modal/>: null}
来销毁
七 尽量写完整的TS
需求变更时, 需要重构代码,字段增减等, 有个 ts 的话,会告诉你哪个文件要修改, 效率和安全性能提升特别特别多,特别是二次封装的组件, 优先级强于业务代码