为什么 useState 触发重渲染后能持续捕获最新 scrollY 值?

6次阅读

为什么 useState 触发重渲染后能持续捕获最新 scrollY 值?

本文解释了 React 中 useState 触发组件重渲染的机制,以及为何在 useEffect 中调用 setTest(scrollY) 后,console.log(scrollY) 能在每次滚动时输出最新值——关键在于重渲染使函数组件体重新执行,从而读取到 dom 当前的 scrollY。

本文解释了 react 中 `usestate` 触发组件重渲染的机制,以及为何在 `useeffect` 中调用 `settest(scrolly)` 后,`console.log(scrolly)` 能在每次滚动时输出最新值——关键在于重渲染使函数组件体重新执行,从而读取到 dom 当前的 `scrolly`。

在 React 函数组件中,组件体(即 JSX 返回前的所有 JavaScript 代码)会在每次渲染时重新执行。这意味着其中访问的任何变量(如 scrollY)都会被实时求值,而非“缓存”为初始值。

回到你的代码:

const [test, setTest] = useState(0);  useEffect(() => {   function handleScroll() {     setTest(window.scrollY); // ? 触发状态更新 → 引发重渲染   }    handleScroll(); // 初始执行一次   window.addEventListener("scroll", handleScroll);    return () => {     window.removeEventListener("scroll", handleScroll);   }; }, []); // 空依赖数组:仅挂载时执行  // ✅ 每次重渲染都会重新执行这行: console.log(window.scrollY); // 输出当前滚动位置(非旧值!)

? 关键机制解析

  • setTest(window.scrollY) 并不直接“保存”或“传递” scrollY 给 test;它只是发起一次状态更新请求
  • React 接收到该请求后,会计划一次新的渲染(re-render)
  • 新渲染开始时,整个组件函数体重新运行,console.log(window.scrollY) 再次执行 —— 此时 window.scrollY 是滚动事件触发后的最新值(例如 327),因此你看到的是实时滚动位置。

⚠️ 注意:scrollY 是一个动态只读属性(window.scrollY),每次访问都返回当前滚动偏移量。它不是 React state,也不受 useState 管理 —— 它只是你在渲染过程中“现场读取”的 DOM 值。

❌ 为什么注释掉 setTest 就只打印一次?

若删除 setTest(window.scrollY),useEffect 内部虽仍注册了滚动监听器,但没有触发任何状态变更,React 不会主动重渲染组件。因此组件仅在初始挂载时渲染一次,console.log(window.scrollY) 也只执行一次(通常是 0)。

✅ 正确实践建议(避免副作用滥用)

虽然上述逻辑可行,但在 JSX 中写 console.log 属于副作用,且违背 React 渲染纯函数原则。更规范的做法是:

useEffect(() => {   const handleScroll = () => {     console.log(window.scrollY); // ✅ 在 effect 内处理副作用   };    window.addEventListener('scroll', handleScroll);   return () => window.removeEventListener('scroll', handleScroll); }, []);

或者,若需将滚动值用于状态或后续逻辑:

const [scrollY, setScrollY] = useState(0);  useEffect(() => {   const handleScroll = () => setScrollY(window.scrollY);   window.addEventListener('scroll', handleScroll);   return () => window.removeEventListener('scroll', handleScroll); }, []);  // 现在 scrollY 是可响应的 React state,可用于条件渲染、动画等 return <div>Scrolled to: {scrollY}px</div>;

✅ 总结

  • useState 本身不“代理”或“监听”外部变量;它的作用是声明状态并驱动重渲染
  • console.log(scrollY) 能持续输出新值,本质是因为 setXXX 触发了重渲染,使组件体反复执行,从而每次都读取最新的 window.scrollY。
  • 真正的响应式数据应通过 useState + useEffect 显式同步,而非依赖副作用式日志来“观察”变化。
  • 避免在渲染体中放置副作用(如 console.log、API 调用),应移入 useEffect 或其他专用 Hook 中管理。

text=ZqhQzanResources