React:组合式组件场景下,子组件如何向父组件传递数据?
在React组合式组件中实现SwitchGroup状态管理
完全可以用React Context来实现这种组合式组件的状态共享,把SwitchGroup的逻辑完全封装在内部,子Switch自动和Group通信,不用手动传递回调函数。下面是具体实现方案:
1. 创建上下文(Context)
首先定义一个上下文,用来在SwitchGroup和它的子Switch之间传递状态和更新方法:
import { createContext, useContext, useState, useId, useEffect, Children, isValidElement } from 'react'; const SwitchGroupContext = createContext(null);
2. 实现SwitchGroup组件
SwitchGroup负责维护组内所有Switch的状态,并处理“不能全关”的核心逻辑:
export function SwitchGroup({ children }) { // 存储每个Switch的状态:key为开关唯一ID,value为是否选中 const [switchStates, setSwitchStates] = useState({}); // 计算当前处于启用状态的开关数量 const enabledCount = Object.values(switchStates).filter(checked => checked).length; // 更新单个开关状态的方法 const updateSwitch = (id, newChecked) => { setSwitchStates(prev => { // 如果要关闭最后一个启用的开关,阻止更新 if (!newChecked && enabledCount === 1) { return prev; } return { ...prev, [id]: newChecked }; }); }; // 初始化子开关的状态:优先使用Switch传入的checked,默认启用 useEffect(() => { const initialStates = {}; Children.forEach(children, child => { if (isValidElement(child)) { const id = child.props.id || useId(); initialStates[id] = child.props.checked ?? true; } }); setSwitchStates(initialStates); }, [children]); return ( <SwitchGroupContext.Provider value={{ switchStates, updateSwitch, enabledCount }}> {children} </SwitchGroupContext.Provider> ); }
3. 实现Switch组件
Switch组件消费上下文,自动获取状态和更新逻辑,无需手动传递回调:
export function Switch({ id: propsId, checked: propsChecked, ...rest }) { // 生成唯一ID,支持用户自定义ID const id = propsId || useId(); const { switchStates, updateSwitch, enabledCount } = useContext(SwitchGroupContext); // 确定当前开关的选中状态:props传入的优先级最高 const currentChecked = propsChecked ?? switchStates[id] ?? true; // 当仅剩一个启用开关时,禁用当前开关的关闭操作 const isDisabled = enabledCount === 1 && currentChecked; const handleChange = (e) => { updateSwitch(id, e.target.checked); }; return ( <label style={{ display: 'block', margin: '8px 0' }}> <input type="checkbox" id={id} checked={currentChecked} onChange={handleChange} disabled={isDisabled} {...rest} /> {rest.children} </label> ); }
4. 使用示例
现在你可以用完全组合式的写法使用组件,不用手动传递任何状态回调:
// 在任意组件中使用 function App() { return ( <div> <h3>开关组示例</h3> <SwitchGroup> <Switch>功能A</Switch> <Switch checked={false}>功能B</Switch> <Switch>功能C</Switch> </SwitchGroup> </div> ); }
核心特性说明
- 完全封装逻辑:所有“不能全关”的判断和状态管理都在SwitchGroup内部,外层组件无需关心。
- 组合式写法:直接用SwitchGroup包裹Switch即可,不用手动传递onChange或状态。
- 灵活性:支持Switch自定义初始状态、ID,也可以自动生成默认值。
- 动态适配:如果动态添加/删除Switch,SwitchGroup会自动更新状态和启用计数。
内容的提问来源于stack exchange,提问作者lemming
相关产品推荐
相关产品推荐

