typescript React类型脚本

50few1ms  于 2023-03-31  发布在  TypeScript
关注(0)|答案(3)|浏览(94)

验证码:

export const HomePage = (): JSX.Element => {
  const refContainer = useRef<HTMLDivElement>(null);
  const [scrollY, setScrollY] = useState<number>(0);
  const { current: elContainer } = refContainer;
  const handleScroll = useCallback(() => {
    if (elContainer) setScrollY(elContainer.scrollTop);
  }, []);
  useEffect(() => {
    document.addEventListener("scroll", handleScroll, { passive: true });
    return () => removeEventListener("scroll", handleScroll);
  }, [handleScroll]);
  return (
    <div className="pageScreen overflow-scroll" ref={refContainer}>
      <Works scrollY={scrollY} />
    </div>
  );
};

ScrollY状态不会改变,因为elContainer为null。我如何解决这个问题?谢谢。

zzlelutf

zzlelutf1#

const { current: elContainer } = refContainer;

在第一次渲染时,您从ref中提取.current属性,并将其保存在变量elContainer中。此值为null,因为页面上还没有任何内容。由于您只设置了一次scroll事件,因此handle scroll将永远使用此null值。
相反,在调用handleScroll之前不要访问.current。这样,第一次渲染就已经完成了,并且refContainer.current已经被变异为具有以下元素:

const handleScroll = useCallback(() => {
    const { current: elContainer } = refContainer; // Moved this inside the function
    if (elContainer) setScrollY(elContainer.scrollTop);
  }, []);
uurv41yg

uurv41yg2#

这应该行得通:

import { FC, useCallback, useEffect, useRef, useState } from 'react'

    export const HomePage: FC = () => {
      const [scrollY, setScrollY] = useState(0)
      const refContainer = useRef<HTMLDivElement>(null)

      const handleScroll = useCallback(() => {
        if (refContainer.current) {
          setScrollY(-refContainer.current.getBoundingClientRect().y)
        }
      }, [])

      useEffect(() => {
        document.addEventListener('scroll', handleScroll)

        return () => {
          document.removeEventListener('scroll', handleScroll)
        }
      }, [handleScroll])

      return (
        <div className="pageScreen overflow-scroll" ref={refContainer}>
          <Works scrollY={scrollY} />
        </div>
      )
    }

总的来说,建议的代码更简洁,更易读,更可靠。它使用FC类型来定义组件,这是FunctionComponent的简写。它使用getBoundingClientRect方法来获取滚动位置,这比使用scrollTop更准确。并且它使用document.removeEventListener在卸载时删除事件侦听器,其更具体且不易出错。

wgeznvg7

wgeznvg73#

useCallback的使用使滚动处理程序记忆,导致它引用旧值elContainer,特别是当它在初始呈现时是null时。
此外,元素的scrollTop仅在滚动元素本身的内容时发生变化,而不是整个文档。因此,scroll事件处理程序应该附加到div,这样可以大大简化代码,如下所示:

export const HomePage = (): JSX.Element => {
  const [scrollY, setScrollY] = useState<number>(0);
  
  const handleScroll = (evt) => setScrollY(evt.currentTarget.scrollTop);

  return (
    <div className="pageScreen overflow-scroll" onScroll={handleScroll}>
      <Works scrollY={scrollY} />
    </div>
  );
};

需要注意的是,要使事件工作,div本身需要能够滚动,而不是文档,因此您还需要使用overflow: automax-height来设置div的样式。
这是一个CodeSandbox demo的行动。

相关问题