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
相关产品推荐
相关产品推荐

