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

ReactJS中如何让列表选中项单独响应外部点击且不触发其余组件渲染

问题根因

  • 父组件的idSelect状态更新时,所有Element子组件没有做渲染拦截,会全部重渲染,因此每个子组件内的console.log都会执行
  • 每个Element组件都单独绑定了全局点击事件,点击外部区域时所有子组件的点击回调都会触发,多次调用setId逻辑冗余,也会额外触发状态更新

修复后完整代码

import "./styles.css";
import React, { useEffect, useState, useRef, memo } from "react";

// 用memo包裹组件,props未变化时不会重渲染
const Element = memo(({ id, setId, selectedId }) => {
  const elRef = useRef(null);

  console.log(id, "checked id");

  // 只有当前组件是选中态时,才绑定全局点击事件监听外部点击
  useEffect(() => {
    if (selectedId !== id) return;

    const handleClick = (e) => {
      // 用ref判断点击是否在当前元素外部,比判断className更准确
      if (!elRef.current.contains(e.target)) {
        // 点击外部后仅当前选中项会执行该逻辑
        console.log(id, "checked id");
        // 若需要点击外部清空选中,可补充 setId(null)
      }
    };

    document.addEventListener("click", handleClick);
    return () => document.removeEventListener("click", handleClick);
  }, [id, selectedId, setId]);

  return (
    <div
      ref={elRef}
      className={"test"}
      onClick={() => {
        setId(id);
      }}
    >
      hello {id}
    </div>
  );
});

export default function App() {
  const [idSelect, setIdSelect] = useState(0);

  return (
    <div className="App">
      {[0, 1, 2].map((el) => {
        // 把当前选中的id传给每个子组件
        return <Element setId={setIdSelect} key={el} id={el} selectedId={idSelect} />;
      })}
    </div>
  );
}

改动说明

  • 用memo包裹Element组件,只有id、setId、selectedId变化时才会重渲染,非选中的子组件不会触发重渲染,也就不会执行多余的console.log
  • 只有处于选中态的子组件才会绑定全局点击事件,点击外部时只会触发选中项的逻辑,不会重复执行
  • 用ref判断点击区域是否在当前元素外,避免className修改导致的判断失效问题

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.02 14:06:04