react-hook-form关联字段用watch时相比useState的性能优势及优化方案
联动场景是否需要用watch?
你的实现思路是对的,这种前序select控制后序可选项的联动场景,确实需要监听前序字段的取值变化来同步更新后序选项,用watch是符合react-hook-form设计逻辑的常规实现方式。
当前写法和纯useState实现有没有性能优势?
你现在贴的这个写法,和纯用2个useState分别存field1、field2的值的实现没有任何性能优势,二者触发组件重渲染的频率完全一致:每次field1/field2变化,整个Example组件都会全量重渲染,甚至因为多了react-hook-form内部的状态同步逻辑,开销还会略高一点点。
即便你用register或者Controller绑定字段,只要没有做额外优化,重渲染的范围也和你自己手写受控组件的范围一致。
怎么优化能保留react-hook-form的性能收益?
有几个落地性很强的优化方案,改完之后性能会明显优于纯useState实现:
- 用
useWatch代替顶层watch,同时把每个select拆成独立的子组件,后序select的组件内部自己监听依赖的前序字段、计算自己的可选项。这样field1变化的时候,只有field2、field3的子组件会重渲染,父组件和其他无关的表单组件完全不会触发重渲染,这是纯useState很难低成本做到的(纯useState要实现局部渲染需要自己搞状态提升或者context,复杂度高很多)。
示例代码参考:import {useForm, Controller, useWatch} from 'react-hook-form' // 单独拆分Field2组件 const Field2 = ({control}) => { const field1val = useWatch({control, name: 'field1'}) // 用useMemo缓存选项计算逻辑 const field2Options = useMemo(() => someLogic(field1val), [field1val]) return <Controller control={control} name="field2" render={({field}) => <select {...field} options={field2Options} />} /> } // 单独拆分Field3组件 const Field3 = ({control}) => { const field2val = useWatch({control, name: 'field2'}) const field3Options = useMemo(() => someOtherLogic(field2val), [field2val]) return <Controller control={control} name="field3" render={({field}) => <select {...field} options={field3Options} />} /> } const Example = () => { const {control, register} = useForm() const field1Options = ['fixed', 'options'] return ( <form> <select {...register('field1')} options={field1Options} /> <Field2 control={control} /> <Field3 control={control} /> </form> ) } - 如果不想拆组件,也可以用
useMemo把options的计算逻辑缓存起来,减少不必要的重复计算,不过这种方式只能降低计算开销,还是避免不了父组件全量重渲染,收益不如拆组件明显。 - 另外哪怕不做性能优化,react-hook-form自带的表单校验、错误收集、提交数据序列化、表单重置等能力都是开箱即用的,不用自己手动写一堆重复逻辑,开发效率的提升非常明显,哪怕渲染性能一致也更推荐用。
内容的提问来源于stack exchange,提问作者Ryo
相关产品推荐
相关产品推荐

