使用 GraphQL Apollo 同时进行轮询和分页?

问题描述 投票:0回答:2
  • 要使用 Apollo 实现分页,您通常会使用发送查询时提供的
    fetchMore
    函数,结合
    updateQuery
    来告诉 Apollo 如何使用结果更新其缓存(将其附加到你已经拥有的)。
  • 要实现近乎实时的同步,您通常会使用轮询,这将每隔 x 秒重新发送您的初始查询。

因此轮询会重新获取您的初始查询并覆盖缓存,因此您会丢失分页以及通过

fetchMore
添加到缓存中的所有
updateQuery
内容。

我的问题:如何同时进行轮询分页,同时保持良好的用户体验? (无需订阅)

graphql apollo react-apollo
2个回答

0
投票

这是我手动实现轮询的方法。它在

onCompleted
中开始轮询,并在
handleFetchMore
中重置。

如果发出带有更改的选项/变量的重新获取查询,则它看起来像是 Apollo 客户端的新查询,因此在加载重新获取查询期间数据将设置为未定义。为了避免轮询重新获取时出现空数据,使用缓冲数据状态。

export const MyComponent = () => {
  // if a refetch-query is issued with changed options / variables, it looks like a new query for Apollo Client and hence the data is set to undefined during loading. To avoid empty data on polling-refetch, a buffered data state is used
  const [bufferedData, setBufferedData] = useState<
    YourDataType | undefined
  >();

  const intervalId = useRef<NodeJS.Timeout | undefined>();
  const resetPolling = () => {
    clearInterval(intervalId.current);
    intervalId.current = undefined;
  };

  const POLL_INTERVAL = 5000;
  const { fetchMore, refetch, variables, networkStatus } =
    useQuery({
      variables: {
        options: {
          filter,
          limit: itemsPerPage,
          offset: 0,
        },
      },
      onCompleted: (data: YourDataType) => {
        if (data) {
          setBufferedData(data);

          // start polling
          const newLastOffset = data?.items.length ?? 0;
          if (!intervalId.current) {
            intervalId.current = setInterval(() => {
              refetch({
                ...variables,
                options: {
                  ...variables?.options,
                  offset: 0,
                  limit: newLastOffset,
                },
              });
            }, POLL_INTERVAL);
          }
        }
      },
      onError: () => setBufferedData(undefined),
    });

  const fetchingMore = networkStatus === NetworkStatus.fetchMore;

  const handleFetchMore = useCallback(() => {
    if (!fetchingMore) {
      resetPolling();
      fetchMore({
        variables: {
          options: {
            filter,
            limit: itemsPerPage,
            offset: bufferedData?.items.length ?? 0,
          },
        },
        updateQuery: (prev, { fetchMoreResult }) => {
          if (!fetchMoreResult || !fetchMoreResult) return prev;

          const merged = [...(prev?.items ?? [])];

          (fetchMoreResult.items ?? []).forEach((item) => {
            if (
              item &&
              !merged.find(
                (mergedItem) => mergedItem && mergedItem.id === item.id
              )
            ) {
              merged.push(item);
            }
          });

          return merged;
        },
      });
    }
  }, [bufferedData?.items.length, fetchMore, fetchingMore]);
};
© www.soinside.com 2019 - 2024. All rights reserved.