【问题标题】:Infinite scroll component using GraphQL and IntersectionObserver使用 GraphQL 和 IntersectionObserver 的无限滚动组件
【发布时间】:2021-03-30 18:47:48
【问题描述】:

我对 React 的经验很少,我正在尝试使用 GraphQL 和 IntersectionObserver 进行无限滚动,一切正常,但我得到的结果是我要求的两倍。我猜它来自双重渲染,一个来自@apollo/client fetchMore,另一个来自 IntersectionObserver 钩子,但我不太明白,也不知道如何修复它。

IntersectionObserver 钩子:

import { useCallback, useRef, useState } from "react";

export default function useIntersection(options?: IntersectionObserverInit): IntersectionHook {
    const [entry, setEntry] = useState<IntersectionObserverEntry>();
    const observer = useRef<IntersectionObserver>();
    const target = useRef<Element | null>(null);

    const setSentinel = useCallback<IntersectionHook[0]>(
        (sentinel) => {
            if (!observer.current) {
                observer.current = new IntersectionObserver((entries) => setEntry(entries[0]), options);
            }

            if (target.current) {
                observer.current.unobserve(target.current);
            }

            target.current = sentinel;

            if (target.current) {
                observer.current.observe(target.current);
            }
        },
        [options],
    );

    return [setSentinel, entry];
}

type IntersectionHook = [(sentinel: Element | null) => void, IntersectionObserverEntry | undefined];

使用钩子的组件:

import { Fragment, ReactElement, ReactNode, useEffect } from "react";
import useIntersection from "../hooks/intersectionHook";

export default function InfiniteComponent(props: InfiniteComponentProps): ReactElement {
    const [setSentinel, entry] = useIntersection();

    useEffect(() => {
        if (entry?.isIntersecting) {
            props.onNext();
        }
    }, [props, entry]);

    return (
        <Fragment>
            {props.children}
            {props.next && <div ref={setSentinel}></div>}
        </Fragment>
    );
}

export interface InfiniteComponentProps {
    next?: boolean;
    onNext: () => Promise<unknown>;
    children: ReactNode;
}

使用该组件的页面:

export default function IndexArtist(): ReactElement | null {
    const { loading, error, data, fetchMore } = useQuery<IndexArtistData, IndexArtistVars>(INDEX_ARTIST, {
        variables: { limit: 3 },
    });

    if (loading) {
        return null;
    }

    if (error) {
        return <Redirect to="/error" />;
    }

    if (!data?.artists.nodes.length) {
        return <Redirect to="/error" />;
    }

    return (
        <BoxElement $variant="page">
            <InfiniteComponent
                next={data.artists.page.next}
                onNext={async () => fetchMore({ variables: { cursor: data.artists.page.cursor } })}
            >
                {/* The code to render */}
            </InfiniteComponent>
        </BoxElement>

使用上面的代码,我请求 3 个结果,并且在加载页面时得到 3 个结果,但是当我向下滚动直到 IntersectionObserver 挂钩的 sentinel 以获取更多结果时,我得到 6 个结果(首先我得到 3 个结果,但是不久之后,几毫秒后,我得到了 3 个结果)。

【问题讨论】:

    标签: reactjs apollo-client intersection-observer


    【解决方案1】:

    我仍然愿意接受建议。下一个解决方案似乎不是更优雅,但它是我发现的唯一一个。

    使用钩子的组件:

    import { Fragment, ReactElement, ReactNode, useEffect, useRef, useState } from "react";
    import useIntersection from "../hooks/intersectionHook";
    
    export default function InfiniteComponent(props: InfiniteComponentProps): ReactElement {
        const [wait, setWait] = useState(false);
        const ready = useRef(!wait);
        const [entry, setSentinel] = useIntersection();
    
        useEffect(() => {
            ready.current = !wait;
        }, [wait]);
    
        useEffect(() => {
            const fetchMore = async (): Promise<void> => {
                setWait(true);
                await props.onMore();
                setWait(false);
            };
    
            if (ready.current && entry?.isIntersecting) {
                fetchMore();
            }
        }, [entry, props]);
    
        return (
            <Fragment>
                {props.children}
                {props.next && !wait && <div ref={setSentinel}></div>}
            </Fragment>
        );
    }
    
    export interface InfiniteComponentProps {
        next?: boolean;
        onMore: () => Promise<unknown>;
        children: ReactNode;
    }
    
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-05-01
      • 1970-01-01
      • 2017-08-14
      • 2014-05-17
      • 2023-03-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多