全部分类
老静吖_7666
2026-05-18
667人浏览
原创
本文详解如何在 React 中正确实现点击添加圆点、点击按钮撤销上一步操作的功能,重点解决事件冒泡导致的 handleUndo 不生效问题,并优化状态更新逻辑。
本文详解如何在 react 中正确实现点击添加圆点、点击按钮撤销上一步操作的功能,重点解决事件冒泡导致的 `handleundo` 不生效问题,并优化状态更新逻辑。
在 React 应用中实现“点击添加圆点 + 撤销上一步”功能时,看似简单的状态管理常因事件冒泡(Event Bubbling) 导致逻辑失效——正如示例代码中,点击 触发 handleUndo() 的同时,其父容器 的 onClick 也被触发,最终新圆点被立即添加,掩盖了撤销效果。✅ 正确解法:阻止事件冒泡或重构 DOM 结构 最直接可靠的修复方式有两种,均能彻底隔离按钮点击与画布点击行为: 方案一:在按钮内调用 event.stopPropagation() <button onclick="{(e)"> { e.stopPropagation(); // 阻止事件向上传播到父 div handleUndo(); }} > Undo </button> 方案二(推荐):将按钮移出可点击画布区域 结构更清晰、语义更明确,避免任何冒泡风险: return ( <button onclick="{handleUndo}">Undo</button> <div classname="circleApp" onclick="{handlePlaceCircle}"> {points.map((point, index) => ( <div key="{index}" classname="point" style="{{" left: point.x top: point.y> o </div> ))} <h1>Hello {name}!</h1> <p>Start editing to see some magic happen :)</p> </div> > ); ? 关键细节提醒: React Component Auditor 审计 React 组件的性能、可访问性、Hook 正确性、属性设计和重渲染优化——识别 memo 优化点和状态管理方案 下载 points.map() 中必须添加 key={index}(或更优的唯一 ID),否则 React 列表渲染会出错或表现异常; handleUndo 使用 [...points].pop() 是可接受的——虽然 pop() 是原生数组方法且会修改副本,但因该副本仅用于本次 setPoints,未污染原始 state,符合不可变更新原则; 若追求极致函数式风格,也可写作:setPoints(points.slice(0, -1)),语义更清晰且完全无副作用。 ✅ 状态更新:Spread 运算符完全适用,无需替换 你当前使用 setPoints([...points, {x, y}]) 和 setPoints(points.slice(0, -1)) 的方式完全正确且高效: ✅ Spread 语法创建新数组,满足 React state 不可变性要求; ✅ 对于中小型列表(百级以内点坐标),性能无瓶颈; ❌ 不需要引入 immer 或 useReducer 等复杂方案——过早优化反而增加认知负担。 ? 完整优化后代码(含健壮性增强) import { FC, useState } from 'react'; import './style.css'; type TPoint = { x: number; y: number }; export const App: FC = ({ name }) => { const [points, setPoints] = useState<tpoint>([]); const handlePlaceCircle = (e: React.MouseEvent<htmldivelement>) => { const { clientX, clientY } = e; setPoints(prev => [...prev, { x: clientX, y: clientY }]); }; const handleUndo = () => { if (points.length === 0) return; // 防止空数组 pop 报错 setPoints(prev => prev.slice(0, -1)); }; return ( <button onclick="{handleUndo}" disabled> Undo ({points.length} items) </button> <div classname="circleApp" onclick="{handlePlaceCircle}"> {points.map((point, index) => ( <div key="{index}" classname="point" style="{{" left: point.x top: point.y> o </div> ))} <h1>Hello {name}!</h1> <p>Start editing to see some magic happen :)</p> </div> > ); };</htmldivelement></tpoint> ? 进阶建议: 为按钮添加 disabled 状态(如示例所示),提升用户体验; 若需支持多步撤销(如“重做”),可引入 history stack 模式(如 useReducer + action 队列),但对基础场景属于过度设计。 至此,你的圆点绘制与撤销功能已稳定、可维护、符合 React 最佳实践。核心要义始终是:理解事件流、尊重不可变性、优先选择简洁解法。
最直接可靠的修复方式有两种,均能彻底隔离按钮点击与画布点击行为:
<button onclick="{(e)"> { e.stopPropagation(); // 阻止事件向上传播到父 div handleUndo(); }} > Undo </button>
结构更清晰、语义更明确,避免任何冒泡风险:
return ( <button onclick="{handleUndo}">Undo</button> <div classname="circleApp" onclick="{handlePlaceCircle}"> {points.map((point, index) => ( <div key="{index}" classname="point" style="{{" left: point.x top: point.y> o </div> ))} <h1>Hello {name}!</h1> <p>Start editing to see some magic happen :)</p> </div> > );
? 关键细节提醒: React Component Auditor 审计 React 组件的性能、可访问性、Hook 正确性、属性设计和重渲染优化——识别 memo 优化点和状态管理方案 下载 points.map() 中必须添加 key={index}(或更优的唯一 ID),否则 React 列表渲染会出错或表现异常; handleUndo 使用 [...points].pop() 是可接受的——虽然 pop() 是原生数组方法且会修改副本,但因该副本仅用于本次 setPoints,未污染原始 state,符合不可变更新原则; 若追求极致函数式风格,也可写作:setPoints(points.slice(0, -1)),语义更清晰且完全无副作用。
? 关键细节提醒:
审计 React 组件的性能、可访问性、Hook 正确性、属性设计和重渲染优化——识别 memo 优化点和状态管理方案
你当前使用 setPoints([...points, {x, y}]) 和 setPoints(points.slice(0, -1)) 的方式完全正确且高效:
import { FC, useState } from 'react'; import './style.css'; type TPoint = { x: number; y: number }; export const App: FC = ({ name }) => { const [points, setPoints] = useState<tpoint>([]); const handlePlaceCircle = (e: React.MouseEvent<htmldivelement>) => { const { clientX, clientY } = e; setPoints(prev => [...prev, { x: clientX, y: clientY }]); }; const handleUndo = () => { if (points.length === 0) return; // 防止空数组 pop 报错 setPoints(prev => prev.slice(0, -1)); }; return ( <button onclick="{handleUndo}" disabled> Undo ({points.length} items) </button> <div classname="circleApp" onclick="{handlePlaceCircle}"> {points.map((point, index) => ( <div key="{index}" classname="point" style="{{" left: point.x top: point.y> o </div> ))} <h1>Hello {name}!</h1> <p>Start editing to see some magic happen :)</p> </div> > ); };</htmldivelement></tpoint>
? 进阶建议: 为按钮添加 disabled 状态(如示例所示),提升用户体验; 若需支持多步撤销(如“重做”),可引入 history stack 模式(如 useReducer + action 队列),但对基础场景属于过度设计。
? 进阶建议:
至此,你的圆点绘制与撤销功能已稳定、可维护、符合 React 最佳实践。核心要义始终是:理解事件流、尊重不可变性、优先选择简洁解法。
相关文章
构建可遍历的嵌套分类树结构:TypeScript + React 实战指南
如何构建可遍历的嵌套分类树结构(支持父子关系与前端渲染)
如何优化使用 useContext 的 React 组件(避免不必要的重渲染)
如何优化使用 useContext 的 React 组件
如何实现HTML单选按钮组与复选框联动
PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!
相关标签:
本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
上一篇:HTML怎么做一行CSS翻转_html一行CSS实现翻转效果【一文搞懂】
下一篇:怎样强制文本换行_br标签实现简单的行内断行效果
作者最新文章
如何可靠检测浏览器自动填充的表单数据并智能初始化
如何在小屏幕下完整显示固定头部中的下拉菜单所有选项
让页面中任意位置的多个按钮自动统一为最宽按钮的宽度
让页面中任意位置的多个按钮自动对齐为最宽按钮的宽度
如何构建兼容性更强的全屏页脚式邮件签名
如何阻止 :active 伪类在子元素(如输入框)点击时触发父容器样式
如何阻止 :active 伪类在子元素(如 input)点击时意外触发
如何在HTML邮件中精准叠放两张图片并消除白边间隙
如何在HTML邮件中精准叠放两张图片(消除默认单元格间距)
如何精准控制 Font Awesome 图标的大小?
热门AI工具
Seko
UP简历
豆包大模型
DeepSeek
蛙蛙写作
讯飞智作
VibeKnow
立刻MV
相关专题
c语言变量名规则是:1、变量名以英文字母开头;2、变量名中的字母是区分大小写的;3、变量名不能是关键字;4、变量名中不能包含空格、标点符号和类型说明符。php中文网还提供c语言变量的相关下载、相关课程等内容,供大家免费下载使用。
2023.06.20
3209
3
C语言是当代人学习及生活中的必备基础知识,应用十分广泛,本专题为大家c语言入门自学零基础的相关文章,以及相关课程,感兴趣的朋友千万不要错过了。
2023.07.25
2328
9
c语言运算符的优先级顺序是括号运算符 > 一元运算符 > 算术运算符 > 移位运算符 > 关系运算符 > 位运算符 > 逻辑运算符 > 赋值运算符 > 逗号运算符。本专题为大家提供c语言运算符相关的各种文章、以及下载和课程。
2023.08.02
1260
5
数据结构是指将数据按照一定的方式组织和存储的方法。它是计算机科学中的重要概念,用来描述和解决实际问题中的数据组织和处理问题。数据结构可以分为线性结构和非线性结构。线性结构包括数组、链表、堆栈和队列等,而非线性结构包括树和图等。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。
2023.08.09
1218
4
c语言random函数用法:1、random.random,随机生成(0,1)之间的浮点数;2、random.randint,随机生成在范围之内的整数,两个参数分别表示上限和下限;3、random.randrange,在指定范围内,按指定基数递增的集合中获得一个随机数;4、random.choice,从序列中随机抽选一个数;5、random.shuffle,随机排序。
2023.09.05
1356
const是关键字,可以用于声明常量、函数参数中的const修饰符、const修饰函数返回值、const修饰指针。详细介绍:1、声明常量,const关键字可用于声明常量,常量的值在程序运行期间不可修改,常量可以是基本数据类型,如整数、浮点数、字符等,也可是自定义的数据类型;2、函数参数中的const修饰符,const关键字可用于函数的参数中,表示该参数在函数内部不可修改等等。
2023.09.20
2198
7
get函数是一个用于从输入流中获取字符的函数。可以从键盘、文件或其他输入设备中读取字符,并将其存储在指定的变量中。本文介绍了get函数的用法以及一些相关的注意事项。希望这篇文章能够帮助你更好地理解和使用get函数 。
3500
8
c语言数组初始化的方法有直接赋值法、不完全初始化法、省略数组长度法和二维数组初始化法。详细介绍:1、直接赋值法,这种方法可以直接将数组的值进行初始化;2、不完全初始化法,。这种方法可以在一定程度上节省内存空间;3、省略数组长度法,这种方法可以让编译器自动计算数组的长度;4、二维数组初始化法等等。
2023.09.22
16035
6
c语言中null和NULL的区别是:null是C语言中的一个宏定义,通常用来表示一个空指针,可以用于初始化指针变量,或者在条件语句中判断指针是否为空;NULL是C语言中的一个预定义常量,通常用来表示一个空值,用于表示一个空的指针、空的指针数组或者空的结构体指针。
549
热门下载
相关下载
精品课程
共0课时 | 195人学习
共58课时 | 12.2万人学习
共12课时 | 1.4万人学习
共6课时 | 54.7万人学习
共89课时 | 133.7万人学习
共49课时 | 82.4万人学习
共22课时 | 2.5万人学习
共31课时 | 3.5万人学习
共28课时 | 4.4万人学习
最新文章