1. 受控组件(Controlled Component)
定义:
组件的状态(如输入框的值、开关的状态等)完全由父组件通过 props 控制,子组件只负责展示和触发事件,所有数据的"源头"都在父组件。
特点:
- 组件的 value/checked 等属性由父组件 state 决定。
- 组件内部通过 onChange 通知父组件,父组件再更新 state,重新传给子组件。
- 数据流是单向的,便于管理和调试。
你的代码中的例子:
<Input value={editPromoCode} onChange={e => setEditPromoCode(e.target.value)} />
如果 editPromoCode 是父组件 state,通过 props 传进来,这就是受控组件。
2. 非受控组件(Uncontrolled Component)
定义:
组件的状态由自己内部 useState/useRef 管理,父组件无法直接控制组件内部的值。需要时可以通过 ref(关于ref的使用,在下面有更详细的解释) 获取当前值。
特点:
- 组件内部自己维护 value/checked。
- 父组件无法直接"控制"组件的值,只能通过 ref 或其他方式"获取"值。
- 适合表单等"只在提交时收集数据"的场景。
例子:
const [inputValue, setInputValue] = useState(''); <Input value={inputValue} onChange={e => setInputValue(e.target.value)} />
如果 inputValue 只在子组件内部,父组件无法直接拿到,这就是非受控组件。
3. 结合你的代码说明
你现在的写法是受控组件:
- 父组件持有 editPromoCode、editAutoDisplay 的 state。
- 子组件通过 setEditPromoCode、setEditAutoDisplay 修改父组件 state。
- 父组件在 save 时直接读取最新 state,提交给后端。
如果你改成非受控组件:
- editPromoCode、editAutoDisplay 只在 PromoCodeButton 内部用 useState 管理。
- 父组件无法直接拿到最新值,只能通过 ref 或 onChange 在特定时机获取。
4. 受控 vs 非受控 适用场景
- 受控组件:需要父组件实时感知、联动、校验、统一管理数据时(如表单校验、多个组件联动)。
- 非受控组件:只在特定时机(如点击保存)收集数据,平时不需要父组件关心内部状态时。
5. 总结
- 受控组件:数据在父,子只展示和触发事件,适合复杂交互和联动。
- 非受控组件:数据在子,父只在需要时获取,适合简单表单或弹窗。
你的当前写法是受控组件,适合需要父组件统一管理 promoCode 状态的场景。
-------- 关于上面提到的ref,以及ref是否使用,什么时候使用 --------------
1. 现在的方式(外部 state 传递 editPromoCode、setEditPromoCode、editAutoDisplay、setEditAutoDisplay)
优点:
- 父组件始终持有最新的编辑态数据,便于统一管理和联动。
- 适合多个组件间需要共享/联动编辑态数据的场景。
- 逻辑清晰,数据流单向,易于调试。
缺点:
- props 变多,组件接口复杂,维护成本上升。
- 父组件代码臃肿,需管理较多 state。
2. ref方式(子组件内部 state,父组件通过ref获取数据)
优点:
- props 简洁,组件更内聚,易于复用和维护。
- 父组件只需在 save 时获取数据,减少无关的 state 管理。
- 适合"只在特定时机(如save)同步数据"的场景。
缺点:
- 破坏了 React 的单向数据流,调试时数据流向不如 props 明确。
- 需要 forwardRef/useImperativeHandle,写法稍复杂。
- 如果有多个地方需要实时拿到编辑态数据,不如 props 方式灵活。
总结建议
- 如果你的业务只需要在 save 时同步数据,且不需要父组件实时感知编辑态数据,推荐用 ref 方式,代码更简洁。
- 如果编辑态数据需要被父组件或其他组件实时感知、联动,建议继续用 props 方式。
实际开发中,ref 方式更适合表单类、弹窗类的"提交时统一收集数据"场景 ,而props 方式更适合全局/多组件联动的复杂场景。
因此,大多数情况下,考虑到组件后续的迭代和可维护性,应该优先选择state来进行状态管理。但考虑到过多的props会让组件变的过于冗长,应该在两者中做一个平衡。