React实现单个按钮点击独立切换状态的解决方案
解决按钮状态同步变化的问题
你的核心问题是用了一个全局布尔状态selected控制所有按钮和行的状态,导致点击任意按钮都会触发全局状态变更,所有元素同步变化。要实现单个按钮独立控制,需要跟踪单个选中商品(单选)或选中商品集合(多选),以下是两种场景的解决方案:
单选场景(一次选一个商品)
修改状态逻辑,用单个变量跟踪当前选中的商品,点击按钮时切换选中状态:
import React from 'react'; import { useState } from 'react'; import 'bulma/css/bulma.css'; import './App.scss'; export const goods = [ 'Dumplings', 'Carrot', 'Eggs', 'Ice cream', 'Apple', 'Bread', 'Fish', 'Honey', 'Jam', 'Garlic', ]; export const App: React.FC = () => { // 跟踪当前选中的商品,null表示未选中 const [selectedItem, setSelectedItem] = useState<string | null>(null); const handleChange = (good: string) => { // 点击已选中的商品则取消,否则选中该商品 setSelectedItem(prev => prev === good ? null : good); } const clearAll = () => { setSelectedItem(null); } return ( <main className="section container"> {selectedItem === null && ( <h1 className="title">No goods selected</h1> )} {selectedItem !== null && ( <h1 className="title is-flex is-align-items-center"> {selectedItem + ' is selected'} <button data-cy="ClearButton" type="button" className="delete ml-3" onClick={clearAll} /> </h1> )} <table className="table"> <tbody> {goods.map((good) => ( <tr data-cy="Good" key={good} className={selectedItem === good ? "has-background-success-light" : ""} > <td> <button data-cy="AddButton" type="button" className="button" onClick={() => handleChange(good)} > {selectedItem === good ? '-' : '+'} </button> </td> <td data-cy="GoodTitle" className="is-vcentered" > {good} </td> </tr> ))} </tbody> </table> </main> ) }
关键修改点
- 移除全局
selected状态,改用selectedItem跟踪单个选中商品 handleChange直接接收商品名称,判断是否已选中来切换状态- 按钮文本和行样式均基于当前商品是否等于
selectedItem - 修正
onClick传参问题:原代码误将事件对象当作商品名称传递,现在直接传入当前商品
多选场景(可选中多个商品)
如果需要支持多选,用Set存储所有选中的商品,点击按钮时切换商品的选中状态:
import React from 'react'; import { useState } from 'react'; import 'bulma/css/bulma.css'; import './App.scss'; export const goods = [ 'Dumplings', 'Carrot', 'Eggs', 'Ice cream', 'Apple', 'Bread', 'Fish', 'Honey', 'Jam', 'Garlic', ]; export const App: React.FC = () => { // 用Set存储选中的商品,保证唯一性 const [selectedItems, setSelectedItems] = useState<Set<string>>(new Set()); const handleChange = (good: string) => { setSelectedItems(prev => { const newSet = new Set(prev); // 切换商品的选中状态 newSet.has(good) ? newSet.delete(good) : newSet.add(good); return newSet; }); } const clearAll = () => { setSelectedItems(new Set()); } return ( <main className="section container"> {selectedItems.size === 0 && ( <h1 className="title">No goods selected</h1> )} {selectedItems.size > 0 && ( <h1 className="title is-flex is-align-items-center"> {Array.from(selectedItems).join(', ') + ' selected'} <button data-cy="ClearButton" type="button" className="delete ml-3" onClick={clearAll} /> </h1> )} <table className="table"> <tbody> {goods.map((good) => ( <tr data-cy="Good" key={good} className={selectedItems.has(good) ? "has-background-success-light" : ""} > <td> <button data-cy="AddButton" type="button" className="button" onClick={() => handleChange(good)} > {selectedItems.has(good) ? '-' : '+'} </button> </td> <td data-cy="GoodTitle" className="is-vcentered" > {good} </td> </tr> ))} </tbody> </table> </main> ) }
关键修改点
- 用
Set<string>存储选中的商品,方便判断和增删操作 - 修改状态时遵循React不可变原则,复制原Set后再修改
- 顶部标题显示所有选中的商品,无选中时显示默认文本
- 按钮和行样式基于当前商品是否存在于
selectedItems集合中
内容的提问来源于stack exchange,提问作者Dukeofduke
相关产品推荐
相关产品推荐

