You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React父组件如何识别被点击的子组件?

可复用React选择组件实现问题

需求背景

我要开发一个可复用React组件,支持在多页面使用,接收多个同类型子组件作为选项。类似react-select的基础用法,但要把「选项」以子元素形式传入,而非通过props的options列表,选中状态和点击回调由父组件管理。

当前实现

当前写法需要在每个子组件上手动绑定selected和onClick:

enum Children {
  first = "first",
  second = "second",
  // ...
}

const [child, setChild] = useState(Children.first)

<ChildrenSelect>
  <Child
    selected={child === Children.first}
    onClick={() => setChild(Children.first)}
  />
  <Child
    selected={child === Children.second}
    onClick={() => setChild(Children.second)}
    // 更多props
  />
  {/* 可能有更多子组件 */}
</ChildrenSelect>

期望简化写法

希望把selected和点击逻辑移到ChildrenSelect组件上,子组件只需要传入标识ID:

const childClickHandler = (c: Children) => setChild(c);

<ChildrenSelect
  selected={child} // 类型为Children
  onChildClick={childClickHandler} // 类型为(c: Children) => void
>
  <Child id={Children.first} />
  <Child id={Children.second} />
  {/* 可能有更多子组件 */}
</ChildrenSelect>

但遇到问题:ChildrenSelect组件如何识别被点击的子组件?


进展与新问题

我通过遍历子组件并修改props的方式实现了初步方案,ChildrenSelect组件代码如下:

// ChildrenSelect
import React, { Children, BaseHTMLAttributes, FC, cloneElement, ReactElement } from "react";

type Props = {
  // ChildrenSelect专属props
} & BaseHTMLAttributes<HTMLDivElement>;

export const ChildrenSelect: FC<Props> = ({ children }) => {
  const childrenArray = Children.toArray(children);

  const handleChildClick = (e) => {
    console.log(e);
  };

  return (
    <div className="children-select">
      <div>{/* 其他父组件内容 */}</div>
      {Children.map(childrenArray, (child, i) => {
        console.log("child is", child);
        return cloneElement(child as ReactElement<ChildComponentProps>, {
          onClick: handleChildClick,
          text: `modified child ${i}`,
        });
      })}
    </div>
  );
};

通过开发者工具能看到子组件已经被设置了新的onClick和text props,但点击子组件时handleChildClick没有触发,无日志输出。

解决方案

问题出在两个核心点:

  1. 子组件需正确转发onClick props:如果Child组件内部没有把传入的onClick绑定到可点击的DOM元素上,父组件设置的回调永远不会触发。示例Child组件写法:
const Child: FC<ChildComponentProps> = ({ onClick, id, text }) => {
  // 必须把onClick绑定到实际可点击的元素上
  return <div onClick={onClick}>{text || id}</div>;
};
  1. 传递子组件ID给父组件回调:要让ChildrenSelect识别点击的子组件,克隆子组件时需把id作为参数传入点击回调,同时自动设置selected状态:
// 修改后的ChildrenSelect组件
import React, { Children, FC, cloneElement, ReactElement } from "react";

type ChildComponentProps = {
  id: Children;
  selected?: boolean;
  onClick?: () => void;
  text?: string;
};

type ChildrenSelectProps = {
  selected: Children;
  onChildClick: (c: Children) => void;
} & React.HTMLAttributes<HTMLDivElement>;

export const ChildrenSelect: FC<ChildrenSelectProps> = ({ children, selected, onChildClick }) => {
  return (
    <div className="children-select">
      <div>{/* 其他父组件内容 */}</div>
      {Children.map(children, (child) => {
        const { id } = (child as ReactElement<ChildComponentProps>).props;
        // 绑定带ID的点击回调
        const handleClick = () => onChildClick(id);
        // 自动设置选中状态
        return cloneElement(child as ReactElement<ChildComponentProps>, {
          onClick: handleClick,
          selected: selected === id,
        });
      })}
    </div>
  );
};

修改后,点击子组件时会正确触发父组件的onChildClick回调并传递对应ID,同时自动维护子组件的selected状态,完全符合期望的简化写法。


内容的提问来源于stack exchange,提问作者Marco Castanho

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 14:20:50