You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React父组件传更新的scrollY给子组件,子组件内部函数取不到最新值如何解决

问题原因
  1. 核心问题是React函数组件的闭包陷阱:函数组件每次渲染都会生成独立的闭包环境,你在回调函数中访问的props是当前渲染周期下的快照值,不会自动随组件更新而变化。
  2. 你子组件中注册的滚动事件回调、递归执行的requestAnimationFrame的step函数,一旦注册完成,就会一直保留创建时的闭包环境,访问的props.scrollY始终是注册时的值:
    • 如果你实际运行的代码给子组件的useEffect加了空依赖数组[],那么useEffect仅在组件首次挂载时执行一次,回调捕获的就是初始值0,后续props更新也不会重新注册回调,所以打印始终为0。
    • 如果你贴的代码是完整的(没加空依赖),问题出在递归调用的step函数始终引用首次注册的函数实例,每次组件重渲染时旧的step还没执行就被清理函数取消,实际运行的始终是最早的step回调,捕获的还是初始值0。
  3. 补充:你父组件的useEffect没有加依赖数组,会导致每次父组件重渲染都反复移除、重新注册全局滚动事件,存在严重性能问题。
解决方法

首先修正父组件的性能问题,给useEffect加空依赖数组,仅在组件挂载时注册一次滚动事件:

// 修正后的父组件useEffect
useEffect(() => {
  function onScroll(e) {
    setScrollY(window.scrollY);
  }
  window.addEventListener("scroll", onScroll);
  return () => {
    window.removeEventListener("scroll", onScroll);
  };
}, []); // 加空依赖,仅挂载/卸载时执行一次

子组件可以选择以下两种方案解决闭包问题:

方案1:给useEffect添加props.scrollY依赖

每次scrollY更新时,销毁旧的回调和requestAnimationFrame实例,重新注册捕获了最新props的新回调:

// 修正后的子组件useEffect
useEffect(() => {
  function onScroll() {
    console.log(props.scrollY);
  }
  function step() {
    console.log(props.scrollY);
    animationFrame = window.requestAnimationFrame(step);
  }
  window.addEventListener("scroll", onScroll);
  let animationFrame = window.requestAnimationFrame(step);

  return () => {
    window.removeEventListener("scroll", onScroll);
    window.cancelAnimationFrame(animationFrame);
  };
}, [props.scrollY]); // 把scrollY加入依赖,变化时重新执行effect

方案2:用useRef存储最新的scrollY值

如果你不想每次props更新都反复注册销毁事件,可以用ref存储最新值,回调中访问ref.current即可拿到最新值,不需要修改useEffect依赖:

import { useEffect, useRef } from 'react';

export default function Child(props) {
  // 定义ref存储最新的scrollY
  const scrollYRef = useRef(props.scrollY);
  // 每次props更新时同步更新ref的值
  scrollYRef.current = props.scrollY;

  useEffect(() => {
    function onScroll() {
      // 访问ref.current拿最新值
      console.log(scrollYRef.current);
    }
    function step() {
      console.log(scrollYRef.current);
      animationFrame = window.requestAnimationFrame(step);
    }
    window.addEventListener("scroll", onScroll);
    let animationFrame = window.requestAnimationFrame(step);

    return () => {
      window.removeEventListener("scroll", onScroll);
      window.cancelAnimationFrame(animationFrame);
    };
  }, []); // 空依赖,仅执行一次即可

  return <div>{props.scrollY}</div>
}

内容的提问来源于stack exchange,提问作者김승어

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.03 13:54:04