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

作者站长头像
站长
· 阅读数 12

在子组件内有:


    // 滚动
    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个回答
avatar
test
2024-08-11

写 React 大家容易陷入一个误区:所有的功能都得通过 props 实现,props 一般用于响应式的更新。例如你这个场景,如果 scrollToIdx 变化触发 scrollTo,scrollToIdx 不变不触发,使用 props 传递参数是合适的。按照你的描述,是希望稳定触发 scrollTo,使用 props 去控制就不合适了,这个时候应该采用命令式调用,而不是响应式更新了。可以使用 ref:

export interface SubContainerRef {
  scrollTo(index: number): void;
}

export const SubContainer = forwardRef<SubContainerRef, SubContainerProps>(
  (props, ref) => {
    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',
        });
      }
    };

    useImperativeHandle(
      ref,
      () => ({
        scrollTo,
      }),
      []
    );
  //....
}

在父组件调用:

  const conRef = useRef<SubContainerRef>();

  const btnClick = () => {
    conRef.current?.scrollTo(20);
  };

  return (
    <div>
      <SubContainer ref={conRef} {...props} />
      <button onClick={btnClick}>点击</button>
    </div>
  );

总结一下:响应式更新使用 props,命令式调用使用 ref。

回复
likes
适合作为回答的
  • 经过验证的有效解决办法
  • 自己的经验指引,对解决问题有帮助
  • 遵循 Markdown 语法排版,代码语义正确
不该作为回答的
  • 询问内容细节或回复楼层
  • 与题目无关的内容
  • “赞”“顶”“同问”“看手册”“解决了没”等毫无意义的内容