
useState 的状态更新会触发组件重渲染,而 console.log 位于 JSX 返回位置(即渲染函数体中),因此每次重渲染都会执行——这正是滚动时持续打印 scrollY 的根本原因;不调用 setTest 则无状态变更,仅初始渲染一次。
react 中 usestate 触发重渲染的机制解析:`usestate` 的状态更新会触发组件重渲染,而 `console.log` 位于 jsx 返回位置(即渲染函数体中),因此每次重渲染都会执行——这正是滚动时持续打印 `scrolly` 的根本原因;不调用 `settest` 则无状态变更,仅初始渲染一次。
在 React 函数组件中,渲染逻辑与状态更新深度绑定。你观察到的现象并非 useState “允许” 访问非 state 属性,而是源于 React 渲染生命周期的本质:只要组件重渲染,其函数体(包括 JSX 中的内联表达式)就会重新执行。
以下代码清晰展示了这一机制:
function ScrollTracker() {
const [test, setTest] = useState(0);
const scrollY = window.scrollY; // 每次渲染时动态读取当前滚动位置
useEffect(() => {
const handleScroll = () => {
setTest(scrollY); // ✅ 触发状态更新 → 引发重渲染
};
handleScroll(); // 初始同步调用(触发第一次渲染)
window.addEventListener('scroll', handleScroll);
return () => window.removeEventListener('scroll', handleScroll);
}, []); // 空依赖数组:仅挂载时设置监听器
// ⚠️ 注意:此 console.log 位于返回的 JSX 中,属于渲染过程的一部分
return <div>{console.log('Current scrollY:', scrollY)}</div>;
}? 关键点解析:
- console.log(scrollY) 写在 return 语句的 JSX 内(如 {console.log(...)}),它不是副作用,而是渲染副作用(render-side effect)——每次组件进入渲染阶段都会执行;
- setTest(scrollY) 改变了 test 的值 → React 检测到状态变更 → 调度一次新的渲染 → 整个函数体重新执行 → scrollY 被重新读取、console.log 再次执行;
- 若注释掉 setTest(scrollY),useEffect 内部虽仍读取 scrollY,但无状态变更 → 无重渲染 → 组件仅在初始化时渲染一次 → console.log 仅执行一次(即使滚动发生)。
✅ 正确做法:若需响应滚动并记录值,请将日志移至可控的副作用中(如 useEffect),或使用 useRef 保存最新值避免冗余渲染:
// 推荐:仅在 scrollY 变化时记录(不触发重渲染)
useEffect(() => {
const handleScroll = () => {
console.log('Scrolled to:', window.scrollY); // ✅ 副作用中记录
};
window.addEventListener('scroll', handleScroll);
return () => window.removeEventListener('scroll', handleScroll);
}, []);
// 或:用 useRef + useEffect 追踪变化(适合需要响应变化的场景)
const scrollYRef = useRef(window.scrollY);
useEffect(() => {
const handleScroll = () => {
const current = window.scrollY;
if (current !== scrollYRef.current) {
console.log('ScrollY changed from', scrollYRef.current, 'to', current);
scrollYRef.current = current;
}
};
window.addEventListener('scroll', handleScroll);
return () => window.removeEventListener('scroll', handleScroll);
}, []);⚠️ 注意事项:
- 避免在 JSX 中放置 console.log、API 调用等副作用逻辑——它们不可预测、难以调试,且违背 React 的声明式设计原则;
- useState 本身不“赋予”访问权限,它只是触发重渲染的开关;真正被反复读取的是 window.scrollY 这一全局值,而重渲染提供了重复执行的机会;
- 所有渲染内联表达式(如 {condition &&
}、{Math.random()})均会在每次渲染时求值——这是 React 函数组件的确定性行为,而非 bug。
总结:理解「渲染即执行」是掌握 React 数据流的关键。useState 不是魔法,它通过调度重渲染,让开发者得以在新渲染周期中安全地读取最新 DOM/环境状态——善用这一机制,配合 useEffect 和 useRef,才能写出高效、可维护的响应式逻辑。










