React TypeScript中如何实现支持传入对象数组的可复用ButtonGroup组件
实现思路
核心是把组件内部的「激活状态维护逻辑」和外部传入的「按钮业务逻辑」完全解耦:
- 激活状态完全由ButtonGroup组件内部自己维护,不需要外部传入的配置携带状态更新函数
- 外部传入的每个按钮配置里只需要带自身的业务点击回调、id、文案这些业务属性即可
- 点击按钮时先触发内部状态更新切换激活态,再执行对应按钮绑定的业务回调
调整后的代码
接口定义
先调整接口,把原来的function改名避免和关键字冲突,简化参数定义:
export interface ButtonItem { id: number; title: string; onClick: () => void; // 每个按钮独立的业务执行函数 } export interface ButtonGroupProps { data: ButtonItem[]; // 外部传入的按钮配置数组 defaultActiveId?: number; // 可选参数,指定默认激活的按钮id,不传默认激活第一个 }
组件实现
import React from 'react'; import { ButtonItem, ButtonGroupProps } from '../../shared/interfaces/ButtonGroup.interface'; const ButtonGroup: React.FunctionComponent<ButtonGroupProps> = ({ data, defaultActiveId }) => { // 内部维护激活状态,存id比存完整对象更稳定,避免引用对比异常问题 const [activeId, setActiveId] = React.useState<number>(() => { if (defaultActiveId) return defaultActiveId; return data[0]?.id ?? 0; }); const getBtnClass = (item: ButtonItem) => { return item.id === activeId ? "active" : "not-active"; }; const handleBtnClick = (item: ButtonItem) => { // 先更新激活状态 setActiveId(item.id); // 再执行按钮绑定的业务函数 item.onClick(); }; return ( <div className="btn-group"> {data.map((item) => ( <button type="button" className={getBtnClass(item)} onClick={() => handleBtnClick(item)} key={item.id} > {item.title} </button> ))} </div> ) } export default ButtonGroup;
调用方式
父组件调用时只需要传入对应的配置数组即可:
// 父组件内定义按钮配置 const buttonConfig = [ { id: 1, title: '按钮1', onClick: () => { console.log('执行按钮1的业务逻辑') } }, { id: 2, title: '按钮2', onClick: () => { console.log('执行按钮2的业务逻辑') } }, { id: 3, title: '按钮3', onClick: () => { console.log('执行按钮3的业务逻辑') } } ] // 调用组件 <ButtonGroup data={buttonConfig} /> // 也可以指定默认激活的按钮 // <ButtonGroup data={buttonConfig} defaultActiveId={2} />
可选优化
如果需要父组件控制激活状态,可以额外加activeId props和onChange回调,把激活状态的控制权交给父组件,满足更复杂的业务场景。
内容的提问来源于stack exchange,提问作者Sole
相关产品推荐
相关产品推荐

