1. 状态管理:从响应式数据到Hooks的思维转换
重构双击爱心组件,第一个要啃的硬骨头就是状态管理。在Vue里,我们习惯了用 ref 和 reactive 声明响应式数据,数据一变,视图自动更新,非常直观。但到了React的世界,特别是用函数组件和Hooks,你得换个思路了。
在原来的Vue组件里,我们大概会这么写:
const likes = ref(0)
const hearts = ref([])
const container = ref(null)
const clickTime = ref(0)
四个变量,清晰明了。likes 记录总点赞数,hearts 数组存着每一个爱心动画的坐标和ID,container 用来拿DOM节点计算点击位置,clickTime 则是判断双击的关键计时器。
迁移到React 19,我们得用 useState 和 useRef 这两个Hook来对应。这里就出现了第一个关键决策点:哪些状态用 useState,哪些用 useRef? 原则其实很简单:需要触发组件重新渲染的数据,用 useState;不需要触发渲染、只是用来存个“快照”或者引用DOM的,用 useRef。
所以,对应关系就出来了:
likes和hearts直接影响UI(一个要显示数字,一个要渲染一堆爱心),所以用useState。containerRef和clickTimeRef不直接导致UI变化(一个只是DOM引用,一个只是内部计时),所以用useRef。
const [likes, setLikes] = useState<number>(0)
const [hearts, setHearts] = useState<Heart[]>([])
const containerRef = useRef<HTMLDivElement>(null)
const clickTimeRef = useRef<number>(0)
我刚开始用React的时候,老喜欢把所有东西都塞进 useState,结果就是组件莫名其妙地频繁重渲染,性能卡顿。后来才明白,useRef 就像是组件的一个“私人抽屉”,里面放的东西变了,不会惊动React去重新画页面,特别适合放那些“幕后工作者”。
比如这个 clickTimeRef,它只在上一次点击和当前点击之间做减法,判断时间差是否小于800毫秒。这个计算过程跟UI一毛钱关系都没有,如果用 useState,每次点击都要触发两次渲染(一次更新clickTime,一次可能更新hearts),纯属浪费。用 useRef 就安静多了,组件该干嘛干嘛,完全不受影响。
2. 事件处理:从模板语法到函数式回调
Vue里处理点击事件,我们通常在模板里写个 @click="handleClick" 就完事了,事件对象 $event 会自动传进去。但在React的JSX里,事件处理是直接以函数的形式绑定的,并且有更严格的类型安全。
在React里,我们这样写:
<div ... onClick={handleClick}>
注意,这里传的是函数名 handleClick,而不是字符串 "handleClick",也不是函数调用 handleClick()。这是一个新手常踩的坑,如果加了括号,函数会在渲染时立即执行,而不是在点击时执行。
事件处理函数本身,也从Vue的选项式写法,变成了一个纯粹的JavaScript/TypeScript函数:
const handleClick = (e: React.MouseEvent) => {
const now = Date.now()
if (clickTimeRef.current === 0) {
clickTimeRef.current = now
} else {
if (now - clickTimeRef.current < 800) {
createHeart(e)
clickTimeRef.current = 0
} else {
clickTimeRef.current = now
}
}
}
这里有个


154

被折叠的 条评论
为什么被折叠?



