簡體   English   中英

Typescript 中的 Intersection Observer 在 useRef 中拋出錯誤

[英]Intersection Observer in Typescript throws error in useRef

我在這里有一個完美的文本動畫。 我現在要添加的是 Intersection Observer,這樣動畫只有在我向下滾動到 Box 時才會開始。

所以我為實現這一點所做的是:我使用 react 鈎子useRef用作對我想要觀察的元素的引用,並將其應用於我的 Box 與ref={containerRef} 然后聲明了一個回調函數,它接收一個 IntersectionObserverEntries 數組作為參數,在這個函數中,我采用第一個也是唯一一個條目並檢查它是否與視口相交,如果是,那么它調用 setIsVisible 的值為 entry.isIntersecting (真假)。 之后,我添加了反應鈎子 useEffect 並使用回調函數和我之前創建的選項創建了一個觀察者構造函數。 我在一個名為useElementOnscreen的新鈎子中實現了邏輯

但是 Typescript 在containerRef?.current告訴我一個錯誤:

Argument of type 'IntersectionObserver' is not assignable to parameter of type 'Element'.
  Type 'IntersectionObserver' is missing the following properties from type 'Element': attributes, classList, className, clientHeight, and 160 more.

而且我不確定如何解決這個錯誤。 我認為這也是我的ref={containerRef}也拋出錯誤的原因

The expected type comes from property 'ref' which is declared here on type 'IntrinsicAttributes & { component: ElementType<any>; } & SystemProps<Theme> & { children?: ReactNode; component?: ElementType<...> | undefined; ref?: Ref<...> | undefined; sx?: SxProps<...> | undefined; } & CommonProps & Omit<...>'

動畫:因此,TopAnimateBlock 和 BottomAnimateBlock 具有 numOfLine 屬性,因此塊內有多少行。 BottomAnimateBlock 中的第二個屬性是 delayTopLine,它應該與 TopAnimateBlock 中的 numOfLine 具有相同的數字,因為我們需要等待頂行播放。

TextAnimation.tsx

import { Box, Stack, Typography } from '@mui/material';
import React, { useRef, useEffect, useState } from 'react';
import styled, { keyframes } from 'styled-components';

const showTopText = keyframes`
  0% { transform: translate3d(0, 100% , 0); }
  40%, 60% { transform: translate3d(0, 50%, 0); }
  100% { transform: translate3d(0, 0, 0); }
`;
const showBottomText = keyframes`
  0% { transform: translate3d(0, -100%, 0); }
  100% { transform: translate3d(0, 0, 0); }
`;

const Section = styled.section`
  width: calc(100% + 10vmin);
  display: flex;
  flex-flow: column;
  padding: 2vmin 0;
  overflow: hidden;
  &:last-child {
    border-top: 1vmin solid white;
  }
`;

const Block = styled.div<{ numOfLine: number }>`
  position: relative;
`;
const TopAnimateBlock = styled(Block)`
  animation: ${showTopText} calc(0.5s * ${props => props.numOfLine}) forwards;
  animation-delay: 0.5s;
  transform: translateY(calc(100% * ${props => props.numOfLine}));
`;
const BottomAnimateBlock = styled(Block)<{ delayTopLine: number }>`
  animation: ${showBottomText} calc(0.5s * ${props => props.numOfLine}) forwards;
  animation-delay: calc(0.7s * ${props => props.delayTopLine});
  transform: translateY(calc(-100% * ${props => props.numOfLine}));
`;

const TextStyle = styled.p<{ color: string }>`
  font-family: Roboto, Arial, sans-serif;
  font-size: 12vmin;
  color: ${props => props.color};
`;


const useElementOnScreen = (options) => {
    const containerRef = useRef<IntersectionObserver | null>(null);
    const [isVisible, setIsVisible] = useState(false);

    const callbackFunction = (entries) => {
        const [entry] = entries;
        setIsVisible(entry.isIntersecting);
    };

    useEffect(() => {
        const observer = new IntersectionObserver(callbackFunction, options);

        if (containerRef.current) observer.observe(containerRef?.current);

        return () => {
            if (containerRef.current) observer.unobserve(containerRef?.current);
        };
    }, [containerRef, options]);

    return [containerRef, isVisible];
};

export function Details() {
    const [containerRef, isVisible] = useElementOnScreen({
        root: null,
        rootMargin: '0px',
        threshold: 1.0,
    });

    return (
    <>
    <Typography>Scroll Down</Typography>
     <Box ref={containerRef}>
      <Section>
        <TopAnimateBlock numOfLine={2}>
          <TextStyle color="grey">mimicking</TextStyle>
          <TextStyle color="white">apple's design</TextStyle>
        </TopAnimateBlock>
      </Section>
      <Section>
        <BottomAnimateBlock numOfLine={1} delayTopLine={2}>
          <TextStyle color="white">for the win!</TextStyle>
        </BottomAnimateBlock>
      </Section>
    </Box>
</>
  );
};

我可以在代碼中大致找到 2 個問題:

首先是這個聲明:

 const containerRef = useRef<IntersectionObserver | null>(null);

通用useRef的實現是通過IntersectionObserver | null完成的。 IntersectionObserver | null 這意味着 ref 容器將保存IntersectionObservernull的實例。 但是 ref 被分配給Box ,(對於那些不熟悉 material-UI 的人,這將是一個div )。 所以應該改為:

  const containerRef = useRef<HTMLDivElement | null>(null);

其次,沒有聲明鈎子的返回類型,並且由於它是一個數組[containerRef, isVisible] ,因此 TS 會自動檢測到它。 Typescript 推斷的類型變為:

鈎子的打字稿返回類型

(boolean | React.MutableRefObject<HTMLDivElement | null>)[]

由於類型實際上是一個元組,並且返回的兩個數組元素都是不同的類型,所以推斷的類型不正確,TS 會抱怨。

在定義鈎子時聲明這一點可以防止 Typescript 抱怨。

const useOnScreen = <T,>(options : T): [MutableRefObject<HTMLDivElement | null>, boolean] => {
  const containerRef = useRef<HTMLDivElement | null>(null);
  const [isVisible, setIsVisible] = useState(false);

  const callbackFunction = (entries) => {
    const [entry] = entries;
    setIsVisible(entry.isIntersecting);
  };

  useEffect(() => {
    const observer = new IntersectionObserver(callbackFunction, options);

    if (containerRef.current) observer.observe(containerRef?.current);

    return () => {
      if (containerRef.current) observer.unobserve(containerRef?.current);
    };
  }, [containerRef, options]);

  return [containerRef, isVisible];
};

解釋元組返回類型和數組返回類型之間區別的鏈接

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM