typescript 相交观察点不会观察多个参考

pod7payv  于 2022-11-18  发布在  TypeScript
关注(0)|答案(2)|浏览(132)

你好,我在下一个js项目中使用switch语句来为页面提供特定的组件。switch语句接收一个有效负载,它循环通过这个有效负载,以导出要提供的组件。这些组件已动态导入,现在我希望使用此动态导入和Intersection Observer在组件进入视口时加载组件,以减少初始页面加载时间并进行拆分我已经加入了一个钩子,它使用交集观察器沿着ref来尝试复制我的想法。现在,当我给予一个div引用时,它会观察到组件进入视口,但是当我给我的div添加多个ref时,我仍然只得到一个div与ref一起被观察。
我做错了什么?我以为你可以多次引用同一个ref,然后只使用.current来标识被观察的当前元素?
Switch陈述式:

import React from 'react';
import getTCTEnv from '../../../lib/helpers/get-tct-env';
import IconWishlistButton from '../../wishlist/add-to-wishlist-button/button-types/icon-wishlist-button';
import loadable from '@loadable/component';
import { useOnScreen } from '../../../hooks/on-screen';

const PriorityCollection = loadable(
  () => import('@culture-trip/tile-ui-module/dist/collectionRail/PriorityCollections'),
  {
    resolveComponent: (components) => components.PriorityCollection
  }
);

const TravelWithUs = loadable(
  () => import('../../../components/trips/travel-with-us/travel-with-us'),
  {
    resolveComponent: (components) => components.TravelWithUs
  }
);

const TrustMessaging = loadable(() => import('../../../components/trips/trust-messaging/index'), {
  resolveComponent: (components) => components.TrustMessaging
});

const PressMessaging = loadable(() => import('../../../components/trips/press-messaging'), {
  resolveComponent: (components) => components.PressMessaging
});

const TripsChatBanner = loadable(
  () => import('../../../components/trips/chat-banner/chat-banner'),
  {
    resolveComponent: (components) => components.TripsChatBanner
  }
);

const HpFeaturedArticles = loadable(
  () => import('../home-page-featured-articles/home-page-featured-articles'),
  {
    resolveComponent: (components) => components.HpFeaturedArticles
  }
);

const InstagramSection = loadable(() => import('../../../components/trips/instagram'), {
  resolveComponent: (components) => components.InstagramSection
});

const EmailForm = loadable(() => import('../../../components/trips/email-form'));

const ReviewsSection = loadable(() => import('../../../components/trips/reviews'));

export const IncludeComponent = ({ collections, reviewData, type }) => {
  const [containerRef, isVisible] = useOnScreen({
    root: null,
    rootMargin: '0px',
    threshold: 0.1
  });

  const instagramCollection = collections.filter((collection) => collection.type === 'instagram');

  const getComponents = () =>
    collections.map((el, i) => {
      switch (el.type) {
        case 'trips':
        case 'article':
          return (
            <PriorityCollection
              key={i}
              collections={[el]}
              tctEnv={getTCTEnv()}
              wishlistButton={<IconWishlistButton />}
            />
          );
        case 'reviews':
          return (
            <>
              <div ref={containerRef} id={i}></div>
              <ReviewsSection reviewData={reviewData} />
            </>
          );
        case 'instagram':
          return (
            <>
              <div ref={containerRef} id={i}></div>
              <InstagramSection collection={instagramCollection} />
            </>
          );
        case 'featured':
          return <PressMessaging />;
        case 'trust':
          return <TrustMessaging type={type} />;
        case 'featuredArticle':
          return <HpFeaturedArticles />;
        case 'email':
          return <EmailForm />;
        case 'chat':
          return <TripsChatBanner />;
        case 'travel':
          return <TravelWithUs type={type} />;
        default:
          return;
      }
    });

  return getComponents();
};

自定义挂钩:

import { useEffect, useState, useRef } from 'react';

export const useOnScreen = (options): any => {
  const containerRef = useRef<HTMLDivElement>(null);
  const [isVisible, setIsVisible] = useState([]);

  const callbackFunction = (entries) => {
    const [entry] = entries;

    if (entry.isIntersecting)
      setIsVisible((oldArray) => [
        ...oldArray,
        isVisible.indexOf(entry.target.id) === -1 && entry.target.id !== undefined
          ? entry.target.id
          : console.log('nothing')
      ]);
  };

  useEffect(() => {
    const observer = new IntersectionObserver(callbackFunction, options);
    if (containerRef.current) observer.observe(containerRef.current);

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

  return [containerRef, isVisible];
};

目前只有instagram引用被观察到

rqmkfv5c

rqmkfv5c1#

如果我对代码的理解是正确的,那么getComponents可能会呈现多个组件。

<div ref={containerRef} id={i}></div>
<ReviewsSection reviewData={reviewData} />
<div ref={containerRef} id={i}></div>
<InstagramSection collection={instagramCollection} />

你希望两个div都能被观察到,但这并不奏效,因为ref本身并不会触发效果。ref只是一个类似{ current: null }的对象。当树被渲染时,containerRef.current被设置为第一个div,然后它被设置为第二个div,然后效果运行。
要执行您想要的操作,您可以:
1.多次调用自定义钩子,并为每个div分配一个containerRef。当然,这里的问题是,您也将有多个IntersectionObserver示例。
1.声明多个引用并通过参数将它们传递给自定义挂接,而不是从自定义挂接返回引用。
1.实现一个callback ref,它将每个div添加到列表中,跳过重复项。这个方法允许你在getComponents中保持相同的实现,但也是钩子最棘手的地方。

8cdiaqws

8cdiaqws2#

解决方法:

import React, { useEffect, useReducer } from 'react';
import getTCTEnv from '../../../lib/helpers/get-tct-env';
import IconWishlistButton from '../../wishlist/add-to-wishlist-button/button-types/icon-wishlist-button';
import loadable from '@loadable/component';
import { useOnScreen } from '../../../hooks/on-screen';

const PriorityCollection = loadable(
  () => import('@culture-trip/tile-ui-module/dist/collectionRail/PriorityCollections'),
  {
    resolveComponent: (components) => components.PriorityCollection
  }
);

const TravelWithUs = loadable(
  () => import('../../../components/trips/travel-with-us/travel-with-us'),
  {
    resolveComponent: (components) => components.TravelWithUs
  }
);

const TrustMessaging = loadable(() => import('../../../components/trips/trust-messaging/index'), {
  resolveComponent: (components) => components.TrustMessaging
});

const PressMessaging = loadable(() => import('../../../components/trips/press-messaging'), {
  resolveComponent: (components) => components.PressMessaging
});

const TripsChatBanner = loadable(
  () => import('../../../components/trips/chat-banner/chat-banner'),
  {
    resolveComponent: (components) => components.TripsChatBanner
  }
);

const HpFeaturedArticles = loadable(
  () => import('../home-page-featured-articles/home-page-featured-articles'),
  {
    resolveComponent: (components) => components.HpFeaturedArticles
  }
);

const InstagramSection = loadable(() => import('../../../components/trips/instagram'), {
  resolveComponent: (components) => components.InstagramSection
});

const EmailForm = loadable(() => import('../../../components/trips/email-form'));

const ReviewsSection = loadable(() => import('../../../components/trips/reviews'));

export const IncludeComponent = ({ collections, reviewData, type }) => {
  const [containerRef, isVisible] = useOnScreen({
    root: null,
    rootMargin: '0px',
    threshold: 0.1
  });

  const instagramCollection = collections.filter((collection) => collection.type === 'instagram');

  const getComponents = () =>
    collections.map((el, i) => {
      switch (el.type) {
        case 'trips':
        case 'article':
          return (
            <PriorityCollection
              key={i}
              collections={[el]}
              tctEnv={getTCTEnv()}
              wishlistButton={<IconWishlistButton />}
            />
          );
        case 'reviews':
          return (
            <>
              <div
                ref={(element) => {
                  containerRef.current[i] = element;
                }}
                id={i}
              ></div>
              {isVisible.indexOf(i.toString()) !== -1 && <ReviewsSection reviewData={reviewData} />}
            </>
          );
        case 'instagram':
          return (
            <>
              <div
                ref={(element) => {
                  containerRef.current[i] = element;
                }}
                id={i}
              ></div>
              <InstagramSection collection={instagramCollection} />
            </>
          );
        case 'featured':
          return <PressMessaging />;
        case 'trust':
          return <TrustMessaging type={type} />;
        case 'featuredArticle':
          return <HpFeaturedArticles />;
        case 'email':
          return <EmailForm />;
        case 'chat':
          return <TripsChatBanner />;
        case 'travel':
          return <TravelWithUs type={type} />;
        default:
          return;
      }
    });

  return getComponents();
};

钩子:

import { useEffect, useState, useRef } from 'react';

export const useOnScreen = (options): any => {
  const containerRef = useRef<HTMLDivElement[]>([]);
  const [isVisible, setIsVisible] = useState([]);

  const callbackFunction = (entries) => {
    const [entry] = entries;

    if (entry.isIntersecting) {
      const checkIdInArray = isVisible.indexOf(entry.target.id) === -1;
      if (checkIdInArray) setIsVisible((oldArray) => [...oldArray, entry.target.id]);
    }
  };

  useEffect(() => {
    const observer = new IntersectionObserver(callbackFunction, options);
    if (containerRef.current)
      containerRef.current.forEach((el) => {
        observer.observe(el);
      });

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

  return [containerRef, isVisible];
};

相关问题