写代码和做人一样,最怕乱动。
你心里没数,手脚就乱。React 也一样,你状态(State)一变,它就慌,它就把整个组件从头到尾再算一遍。就像一个刚谈恋爱的愣头青,姑娘回个“嗯”,他脑子里已经把孩子上哪个幼儿园都想好了。
这种“勤奋”,有时候是灾难。
蝴蝶的躁动
刚才我们在修那个《单词听写》的小工具。背景里有几只蝴蝶,本来挺美,挺诗意。结果你一敲键盘,输入一个字母,蝴蝶就疯了一样满屏乱闪。
为什么?因为蝴蝶的位置是随机生成的:
// 愣头青写法
const flowers = [...Array(12)].map(() => ({
left: `${Math.random() * 100}%`, // 每次都随机
top: `${Math.random() * 100}%`
}));
你敲键盘,userInput 变了,组件重绘(Re-render)。React 走到这一行,一看 Math.random(),心想:“得嘞,主子要随机,我就再给你随一个。”
于是,每一次按键,都是一次宇宙重炸,蝴蝶在毫秒之间从东半球瞬移到西半球。这不叫灵动,这叫多动症。这叫由于内心不笃定而引发的生理性震颤。
useMemo:代码里的定力
怎么治?得有定力。
在 React 里,这个定力叫 useMemo。
Memo 是什么?是 Memory,是记忆。是你在无常的时间流里,抓住的那一点点确定的东西。
我们要告诉 React:“这些蝴蝶的位置,算一次就够了。除非天塌下来(依赖项变了),否则别动它。”
于是我们加上了这把椅子:
// 有定力的写法
const decorations = React.useMemo(() => {
// 这里面的代码,只会在组件初次见面时执行一次
// 就像初恋,只有一次,后面都是过日子
return {
flowers: [...Array(12)].map(() => ({
left: `${Math.random() * 100}%`,
top: `${Math.random() * 100}%`
})),
butterflies: [...Array(6)].map(() => ({
// ...
}))
};
}, []); // 空数组,意味着心如止水,谁变我都不变
加上 useMemo 之后,你再敲键盘,userInput 随便变,React 走到这儿,会从兜里掏出上次算好的那张纸条,说:“还是这几只蝴蝶,位置没变,拿去用。”
蝴蝶不动了,心就静了。屏幕不抖了,世界就美好了。
什么时候用?
别滥用。
有些程序员,学会了 useMemo,恨不得连 1 + 1 都给 Memo 起来。那是另一种病,叫“过度优化”,叫“吝啬”。
只有两种情况值得你动用这份定力:
- 计算太贵:算一次要半条命,比如处理几万条数据,或者复杂的数学运算。这时候要省着点用脑子。
- 引用太脆:就像我们这次。计算虽然不贵(生成几个随机数而已),但我们需要引用的稳定性。如果每次都生成新的对象,会导致子组件以为数据变了,跟着瞎折腾,或者像这次一样,导致视觉上的“抖动”。
结尾
代码写到最后,拼的不是手速,是心性。
知道什么时候该动,什么时候不该动,这就是高级。
useMemo 就是 React 给你的那个“不乱动”的权利。用好了,你的代码就有了金线;用不好,就是给本来就复杂的逻辑里又塞了一团乱麻。
蝴蝶飞得再花哨,落脚点得稳。
就这样。