复用动态Select组件onChange触发多事件,是否需拆分组件?
问题解答
核心结论
不需要为每个select单独创建组件,你的问题和this完全无关(函数组件里this本身就是undefined,你控制台打印的this也能证明这点),真正的问题是每个FlightTypeComponent实例都独立初始化了useForm钩子,导致表单状态和逻辑重复,所以两个组件触发事件都会输出内容。
问题分析
- 函数组件里的
this:你写的是函数组件,这类组件没有类组件的this上下文,所以console.log(this)打印出来必然是undefined,完全不用怀疑是this导致两个组件关联的问题。 - useForm重复初始化的影响:每个
FlightTypeComponent都调用了useForm,意味着每个组件都有自己独立的表单状态、register/unregister/setValue等方法。这就导致两个组件的表单逻辑完全独立,各自触发onChange时,都会执行自己的regionSetter和console.log(watch()),所以控制台会多次输出内容。
解决方案
把useForm提升到父组件中,将表单相关的方法通过props传递给FlightTypeComponent,让所有组件共享同一个表单状态:
父组件示例
import { useForm } from "react-hook-form"; const ParentComponent = () => { // 只在父组件初始化一次useForm const { register, unregister, watch, setValue, formState: {errors} } = useForm({ defaultValues: { flights: { domestic: "", international: "" } } }); const {flightType, flightTypeClick} = useBookingContext(); console.log(watch()); return ( <div> {/* 传递表单方法给子组件 */} <FlightTypeComponent flights={yourFlightsData} flightLocalType="domestic" unregister={unregister} setValue={setValue} flightType={flightType} flightTypeClick={flightTypeClick} /> <FlightTypeComponent flights={yourFlightsData} flightLocalType="international" unregister={unregister} setValue={setValue} flightType={flightType} flightTypeClick={flightTypeClick} /> </div> ); };
修改后的FlightTypeComponent
// 接收父组件传递的表单方法 const FlightTypeComponent = ({flights, flightLocalType, unregister, setValue, flightType, flightTypeClick}) => { const regionSetter = (regionType, regionValue) => { if (regionType === "international") { unregister("flights.domestic"); } if (regionType === "domestic") { unregister("flights.international"); } setValue(`flights.${regionType}`, regionValue); flightTypeClick(regionType, regionValue); }; return ( <div className="flex flex-col"> <label className='absolute z-10'>{flightLocalType}</label> <select className={`${flightType === flightLocalType ? `bg-amber-200` : `bg-white`} relative h-20`} onChange={(e) => regionSetter(flightLocalType, e.currentTarget.value)} > <option value="">___</option> {flights.map(({region, location}) => ( {/* 给optgroup加上key,避免React警告 */} <optgroup key={region} label={region}> {Object.keys(location).map((eachProvince, index) => ( <option key={index} value={eachProvince}>{eachProvince}</option> ))} </optgroup> ))} </select> </div> ); };
额外优化点
- 删掉
regionSetter里的console.log(this),函数组件里它没有任何实际作用; - 清理了原select className里多余的单引号(原代码中
'relative h-20 '的单引号是冗余的); - 给
optgroup添加了key属性,解决React列表渲染的警告问题。
内容的提问来源于stack exchange,提问作者Johann Mendoza
相关产品推荐
相关产品推荐

