当前位置: 首页 > 知识库问答 >
问题:

javascript - useEffect(..., [props.scrollToIdx])) 要如何才能每次都执行?

袁何平
2024-08-04

在子组件内有:


    // 滚动
    useEffect(() => {
        if(props.scrollToIdx) {

            const scrollToIdx = props.scrollToIdx 
            
            console.log("滚动ID:", '#item-' + scrollToIdx!.toString())

            scrollTo(scrollToIdx)
        }
    }, [props.scrollToIdx])

    const scrollTo = (scrollToIdx: number) => {
        const container = document.querySelector("#container")
        const div: HTMLDivElement | null = document.querySelector('#item-' + scrollToIdx!.toString())
    
        console.log(container, div)
        if(container) {
            container.scrollTo({
                top: div!.offsetTop,
                behavior: 'smooth'
            });
        }
    }

在父组件

点击按钮时候会滑动:

  const btnClick = () => {
    console.log('点击按钮')
    setSelIdx(20)
  }

  return (
    <div className="App">
      
      <SubContainer {...props}></SubContainer>
    
      <button onClick={btnClick}>点击</button>
    </div>
  );

但是比如我在点击一次setSelIdx(20)之后,再点击,执行:
setSelIdx(20)

子组件就不会更新再继续执行这个代码了:

    useEffect(() => {
        if(props.scrollToIdx) {

            const scrollToIdx = props.scrollToIdx 
            
            console.log("滚动ID:", '#item-' + scrollToIdx!.toString())

            scrollTo(scrollToIdx)
        }
    }, [props.scrollToIdx])
也就是说:props.scrollToIdx 没有更新这里就不会执行,请问要如何保证父组件点击(btnClick ... setSelIdx(20))
子组件这里每次都执行?

共有1个答案

舒枫涟
2024-08-04

为了确保 useEffect 钩子在 props.scrollToIdx 更改时总是执行,你需要确保 props.scrollToIdx 的值在每次点击时确实发生了变化。从你的描述来看,问题可能出在 props.scrollToIdx 的值在第二次点击时并没有真正改变(即它仍然是 20),因此 React 认为依赖项没有变化,因此不会重新运行 useEffect

要解决这个问题,你可以考虑以下几种方法:

  1. 确保 props.scrollToIdx 的值在每次点击时都不同
    如果 scrollToIdx 总是指向同一个值(如 20),则 useEffect 不会重新运行。你可以通过添加一个时间戳或递增的计数器来确保每次 scrollToIdx 都不同。
  2. 使用 useStateuseEffect 在子组件中控制滚动
    如果父组件仅仅是为了触发滚动,你可以考虑在子组件内部使用 useState 来控制滚动,而不是直接从 props 中获取。

方法 1: 更改 props.scrollToIdx 的值

在父组件中,你可以添加一个额外的状态来确保 scrollToIdx 每次都不同:

const [scrollCounter, setScrollCounter] = useState(0);

const btnClick = () => {
  console.log('点击按钮');
  setSelIdx(20);
  setScrollCounter(prev => prev + 1); // 更新计数器
}

// 传递 scrollCounter 或结合 selIdx 和 scrollCounter 作为新的 prop
return (
  <div className="App">
    <SubContainer scrollToIdx={[selIdx, scrollCounter]}></SubContainer>
    <button onClick={btnClick}>点击</button>
  </div>
);

然后在子组件中,你需要相应地调整 useEffect 依赖项和滚动逻辑。

方法 2: 在子组件中使用 useState

在子组件中,你可以使用 useState 来存储 scrollToIdx 的值,并在父组件触发时更新它:

// 子组件
const [localScrollToIdx, setLocalScrollToIdx] = useState(props.initialScrollToIdx || null);

useEffect(() => {
  if (props.scrollToIdx !== undefined) {
    setLocalScrollToIdx(props.scrollToIdx);
  }
}, [props.scrollToIdx]);

useEffect(() => {
  if (localScrollToIdx) {
    console.log("滚动ID:", '#item-' + localScrollToIdx.toString());
    scrollTo(localScrollToIdx);
  }
}, [localScrollToIdx]);

// ... 其余代码 ...

在父组件中,你只需传递初始的 scrollToIdx(如果需要的话),并在需要时更新它:

const [selIdx, setSelIdx] = useState(null);

const btnClick = () => {
  console.log('点击按钮');
  setSelIdx(20);
}

return (
  <div className="App">
    <SubContainer initialScrollToIdx={selIdx}></SubContainer>
    <button onClick={btnClick}>点击</button>
  </div>
);

这样,每次 selIdx 更新时,都会通过 props 传递给子组件,子组件的 useEffect 钩子将检测到 props.initialScrollToIdx 的变化(如果作为依赖项),但更推荐的做法是使用内部状态 localScrollToIdx 来控制滚动行为。

 类似资料:
  • 在进行单元测试时,执行一个测试类就要启动springboot项目,加载上下文数据,每次执行一次测试都要再重新加载上下文环境.这样导致每一次单元测试时都会花3-5分钟时间。如何解决这个问题? 网上找不到什么简单的可行方案

  • vue3 中说 createApp 只能调用一次,可是我现在有两个场景需要手动挂载 组件的情况: 场景一 手动挂载弹窗,比如我手动挂载一个按钮组件: vue3 中不再支持 extend, 因此我使用createApp代替,可是第二次调用createApp的返回值和第一次调用的返回值不同,第二次的返回值没有umount方法,挂载后无法手动卸载应用。 场景二 有你一个表格,我使用render函数自定义

  • vue2开发 需求是没间隔多上时间请获取一次token、token是10分钟有效、我需要在7分钟的时候刷新一次token、然后我使用的是setInterval()方法 没7分钟请求一次, 但是出现的问题是、页面刷线以后setInterval()方法又重新执行了一次、导致没有正常刷新token 如何解决setInterval页面刷新以后不再重新执行 或者利用其他方法

  • uniapp 为什么每次进来都会执行onLoad钩子? 症状:不管是第一次还是第N次进去都会执行onload,印象中应该只有第一次进来才会执行一次吧,有谁遇到过吗,难道是用redirectTo跳转才会这样吗

  • 用的是vue-element-admin架子. 点击一个菜单连接后打开一个列表(A.create), 点击列表中的一个项(B.create)打开B页面. 再关闭B页面, 此时再回到A, 此时又执行了A页面的create方法 开发时以上操作不会触发A页面的create, 现在莫名的都create了

  • 我正在运行带有 WSL2 的视窗 10。我正在使用带有远程 - WSL扩展名的VSCode从我的wsl文件系统中打开文件。 当我启动我的Windows笔记本电脑并打开VSCode时,我收到以下错误: 当我执行<code>wsl时。exe——关闭PowerShell中的</code>,然后重新启动Docker Desktop,一切正常。但每次笔记本电脑重启后,我都必须这样做。 远程WSL扩展版本:v