React多实例Buttons组件点击状态管理与跨组件传递实现
问题描述
现有如下结构的Buttons组件,包含2种Type,每种Type下有2种SubType的按钮,组件接收唯一id属性:
export function Buttons({id}) { return ( <> /* Type 1 --- SubType 1.1 */ <button onClick={() => {setClButtons(???)}} data-label="subtype-11-left">left</button> <button onClick={() => {setClButtons(???)}} data-label="subtype-11-center">center</button> <button onClick={() => {setClButtons(???)}} data-label="subtype-11-right">right</button> /* Type 1 --- SubType 1.2 */ <button onClick={() => {setClButtons(???)}} data-label="subtype-12-top">top</button> <button onClick={() => {setClButtons(???)}} data-label="subtype-12-middle">middle</button> <button onClick={() => {setClButtons(???)}} data-label="subtype-12-bottom">bottom</button> /* Type 2 --- SubType 2.1 */ <button onClick={() => {setClButtons(???)}} data-label="subtype-21-left">left</button> <button onClick={() => {setClButtons(???)}} data-label="subtype-21-center">center</button> <button onClick={() => {setClButtons(???)}} data-label="subtype-21-right">right</button> /* Type 2 --- SubType 2.2 */ <button onClick={() => {setClButtons(???)}} data-label="subtype-22-top">top</button> <button onClick={() => {setClButtons(???)}} data-label="subtype-22-middle">middle</button> <button onClick={() => {setClButtons(???)}} data-label="subtype-22-bottom">bottom</button> </> ) }
需求如下:
- 将每个按钮的点击状态存储到React状态钩子中,状态结构可自定义;
- 存在
ClickedButtons组件,需将同一id的Buttons组件中被点击的按钮数据传递给对应id的ClickedButtons组件,实现同ID组件的点击状态关联展示。例如点击id为3的Buttons组件中data-label="subtype-22-middle"的按钮,该按钮需在id为3的ClickedButtons组件中显示。
组件使用结构:
<ClickedButtons id={1} /> <ClickedButtons id={2} /> <ClickedButtons id={3} /> <Buttons id={1} /> <Buttons id={2} /> <Buttons id={3} />
预期效果:点击id为1的Buttons组件中的subtype-11-center和subtype-21-right按钮时,id为1的ClickedButtons组件展示:
<h2>ClickedButtons ID 1:</h2> <button data-label="subtype-11-center">center</button> <button data-label="subtype-21-right">right</button>
实现方案
1. 状态管理设计
采用对象结构的状态存储不同id对应的已点击按钮集合,键为组件id,值为该id下已点击按钮的data-label集合(用Set自动去重,避免重复点击同一按钮多次添加)。
2. 父组件统一管理状态
由于Buttons和ClickedButtons是同级组件,需在它们的共同父组件中维护状态,并通过props传递状态和更新方法,实现跨组件的状态同步。
3. 具体代码实现
父组件(状态容器)
import { useState } from 'react'; import Buttons from './Buttons'; import ClickedButtons from './ClickedButtons'; export default function ParentComponent() { // 状态结构:{ [id]: Set<string> },用Set自动处理重复点击 const [clickedButtons, setClickedButtons] = useState({}); // 处理按钮点击的统一方法 const handleButtonClick = (componentId, label) => { setClickedButtons(prev => { // 获取当前id对应的已点击集合,无则新建空Set const currentSet = prev[componentId] || new Set(); // 切换状态:已存在则移除,不存在则添加 currentSet.has(label) ? currentSet.delete(label) : currentSet.add(label); // 返回新状态对象,避免直接修改原状态 return { ...prev, [componentId]: new Set(currentSet) }; }); }; return ( <div> <ClickedButtons id={1} clickedLabels={clickedButtons[1] || new Set()} /> <ClickedButtons id={2} clickedLabels={clickedButtons[2] || new Set()} /> <ClickedButtons id={3} clickedLabels={clickedButtons[3] || new Set()} /> <Buttons id={1} onButtonClick={handleButtonClick} /> <Buttons id={2} onButtonClick={handleButtonClick} /> <Buttons id={3} onButtonClick={handleButtonClick} /> </div> ); }
改造后的Buttons组件
export function Buttons({ id, onButtonClick }) { // 统一处理按钮点击,获取当前按钮的data-label const handleClick = (e) => { const label = e.target.dataset.label; onButtonClick(id, label); }; return ( <> {/* Type 1 --- SubType 1.1 */} <button onClick={handleClick} data-label="subtype-11-left">left</button> <button onClick={handleClick} data-label="subtype-11-center">center</button> <button onClick={handleClick} data-label="subtype-11-right">right</button> {/* Type 1 --- SubType 1.2 */} <button onClick={handleClick} data-label="subtype-12-top">top</button> <button onClick={handleClick} data-label="subtype-12-middle">middle</button> <button onClick={handleClick} data-label="subtype-12-bottom">bottom</button> {/* Type 2 --- SubType 2.1 */} <button onClick={handleClick} data-label="subtype-21-left">left</button> <button onClick={handleClick} data-label="subtype-21-center">center</button> <button onClick={handleClick} data-label="subtype-21-right">right</button> {/* Type 2 --- SubType 2.2 */} <button onClick={handleClick} data-label="subtype-22-top">top</button> <button onClick={handleClick} data-label="subtype-22-middle">middle</button> <button onClick={handleClick} data-label="subtype-22-bottom">bottom</button> </> ); }
ClickedButtons组件
// 统一维护标签与显示文本的映射,避免硬编码 const labelToText = { 'subtype-11-left': 'left', 'subtype-11-center': 'center', 'subtype-11-right': 'right', 'subtype-12-top': 'top', 'subtype-12-middle': 'middle', 'subtype-12-bottom': 'bottom', 'subtype-21-left': 'left', 'subtype-21-center': 'center', 'subtype-21-right': 'right', 'subtype-22-top': 'top', 'subtype-22-middle': 'middle', 'subtype-22-bottom': 'bottom' }; export function ClickedButtons({ id, clickedLabels }) { return ( <div> <h2>ClickedButtons ID {id}:</h2> {Array.from(clickedLabels).map(label => ( <button key={label} data-label={label}> {labelToText[label]} </button> ))} </div> ); }
关键说明
- 使用
Set存储已点击标签,自动处理重复点击的去重问题,点击同一按钮可切换显示/隐藏状态; - 父组件作为状态中心,通过props传递状态和更新方法,实现同级组件的状态同步;
labelToText映射表统一管理标签与显示文本,避免组件内硬编码,便于后续维护和修改。
内容的提问来源于stack exchange,提问作者s.kuznetsov
相关产品推荐
相关产品推荐

