useRef钩子无法及时更新的原因?用于状态设置与Emotion.js修改CSS
useRef 确实会出现“看起来没及时更新”的情况,核心和它的设计特性、你的使用方式直接相关,结合你用 Emotion.js 修改 CSS 的场景,具体拆解如下:
核心原因:useRef 的特性与使用误区
useRef 不触发组件重渲染:useRef 的
.current属性是可变的,但它的更新不会触发组件重新渲染。如果你用它来管理需要驱动 UI(比如 Emotion 样式)的“状态”,组件不会因为.current变化而重新执行,依赖它的 CSS 自然不会同步更新。
错误用法示例:const sizeRef = useRef(50); // 修改ref,但组件不重渲染 const handleClick = () => { sizeRef.current += 50; }; // Emotion的样式只会在组件初始渲染时计算一次,后续不会更新 const boxStyle = css` width: ${sizeRef.current}px; height: ${sizeRef.current}px; background: blue; `;点击按钮后,
sizeRef.current确实变化了,但组件没重渲染,boxStyle不会重新计算,样式毫无变化。更新时机与渲染周期不匹配:如果在组件渲染完成后的回调(比如
setTimeout、DOM 事件回调)里更新 useRef,此时组件已经完成渲染,依赖 useRef 的样式早已计算完毕,除非手动触发重渲染,否则样式不会同步。
结合 Emotion.js 的正确做法
1. 用 useState 管理驱动样式的状态
如果某个值需要同步影响 Emotion 的 CSS,优先用 useState 而非 useRef——useState 的更新会触发组件重渲染,让 Emotion 的 css 函数重新计算样式:
const [size, setSize] = useState(50); const handleClick = () => { setSize(prev => prev + 50); }; // 每次组件重渲染时,都会基于最新的size计算样式 const boxStyle = css` width: ${size}px; height: ${size}px; background: blue; `;
2. 必须用 useRef 时,手动触发重渲染
如果场景确实需要用 useRef(比如存储 DOM 元素、无需频繁重渲染的可变值),但又要同步更新样式,可以搭配一个空的 useState 来强制触发重渲染:
const sizeRef = useRef(50); const [, forceRender] = useState({}); const handleClick = () => { sizeRef.current += 50; // 通过更新一个空对象触发重渲染 forceRender({}); }; // 重渲染时,会基于最新的sizeRef.current计算样式 const boxStyle = css` width: ${sizeRef.current}px; height: ${sizeRef.current}px; background: blue; `;
关键提醒
Emotion 的 css 函数是在组件渲染阶段执行的,只有组件重渲染时,它才会重新计算样式。所以任何需要驱动样式的变量,都应该用能触发重渲染的状态管理方案(useState/useReducer),而非 useRef。
内容的提问来源于stack exchange,提问作者Silicii

