React表单点击按钮组提交事件失效及按钮类型切换需求
问题描述
为表格过滤功能制作了一个按钮组,仅用于切换激活按钮的样式,因此把表格和按钮组包裹在form标签中(表格过滤可能用到多个按钮组)。但点击轮廓样式按钮时,onSubmit事件无法触发,只有在按钮组的onClick中修改状态时才会出现这个问题。
问题更新排查
经排查,当使用getUniqueId()(基于uuid v4生成ID)作为key,或在点击处理函数中修改按钮类型(即使是原生按钮)时会出现该问题。点击处理函数的执行早于提交事件。现在的需求是:点击同一按钮时,将其类型从submit改为button以避免重复触发提交事件,同时不丢失正常的提交事件。
相关代码
ButtonGroup组件
interface ButtonGroupProps { labels: string[]; } const ButtonGroup: FC<ButtonGroupProps> = ({ labels }) => { const [active, setActive] = useState<number>(0); const handleClick = (index: number) => () => { setActive(index); } return ( <GroupWrapper> {labels.map((label, index) => { const props: ButtonProps = active === index ? ({ variant: 'contained', type: 'button', }) : ({ variant: 'outlined', type: 'submit', onClick: handleClick(index), }) return ( <Button key={getUniqueId()} text={label} borderRadius='16px' padding='2px 12px' borderWidth='2px' {...props} /> )})} </GroupWrapper> ) } export default ButtonGroup
包含表格的表单组件
interface InfoTableProps extends TableProps { fields: InfoTableField[]; filterGroups?: [string[]]; } const InfoTable: FC<InfoTableProps> = ({ fields, filterGroups, ...rest }) => { const handleSubmit = (event: FormEvent) => { event.preventDefault() console.log('button change') } return ( <InfoTableForm onSubmit={handleSubmit}> {filterGroups?.map((filters) => ( <ButtonGroup key={getUniqueId()} labels={filters} /> ))} <Table {...rest}> <TableBody> {fields.map(({ head, value }) => ( <BodyTableRow key={getUniqueId()}> <TableCell component='th'> {head} </TableCell> <TableCell> {value instanceof Date ? value.toLocaleDateString() : value} </TableCell> </BodyTableRow> ))} </TableBody> </Table> </InfoTableForm> )} export default InfoTable
解决方案
核心思路
- 固定key值:放弃使用
getUniqueId()作为key,改用数组索引或稳定标识,避免组件每次渲染都重新挂载,导致事件绑定异常。 - 统一按钮类型为submit:不再动态切换按钮类型,而是通过点击逻辑判断是否需要触发提交:
- 点击已激活按钮时,阻止默认提交行为;
- 点击未激活按钮时,先更新激活状态,再允许提交事件正常触发。
修改后的ButtonGroup组件
interface ButtonGroupProps { labels: string[]; } const ButtonGroup: FC<ButtonGroupProps> = ({ labels }) => { const [active, setActive] = useState<number>(0); const handleClick = (index: number) => (e: React.MouseEvent<HTMLButtonElement>) => { // 点击已激活按钮,阻止重复提交 if (active === index) { e.preventDefault(); return; } // 点击未激活按钮,更新激活状态 setActive(index); } return ( <GroupWrapper> {labels.map((label, index) => ( <Button key={index} // 改用索引作为稳定key text={label} borderRadius='16px' padding='2px 12px' borderWidth='2px' variant={active === index ? 'contained' : 'outlined'} type='submit' // 统一为submit类型 onClick={handleClick(index)} /> ))} </GroupWrapper> ) } export default ButtonGroup
修改后的表单组件(仅key部分)
// ...其他代码不变 {filterGroups?.map((filters, groupIndex) => ( <ButtonGroup key={groupIndex} labels={filters} /> ))} // ...表格行key改为索引 {fields.map(({ head, value }, fieldIndex) => ( <BodyTableRow key={fieldIndex}> <TableCell component='th'> {head} </TableCell> <TableCell> {value instanceof Date ? value.toLocaleDateString() : value} </TableCell> </BodyTableRow> ))}
方案解释
- 固定key:使用数组索引作为key(若labels不会动态增删,索引是稳定的),保证组件不会因key变化频繁卸载重建,避免事件绑定丢失或异常。
- 统一submit类型:所有按钮保持
submit类型,点击时先判断状态:- 已激活按钮:阻止默认提交,避免重复触发;
- 未激活按钮:更新激活状态后,表单会自动触发
onSubmit,不会影响正常提交流程。
这样既实现了点击同一按钮不重复提交,又保证了切换按钮时正常触发表单提交,同时解决了之前onSubmit无法触发的问题。
内容的提问来源于stack exchange,提问作者Resaqw
相关产品推荐
相关产品推荐

