我尝试使用useSWRInfinite实现无限滚动钩。
从Youtube教程👈中找到一段代码,并做了一些修改。
但是得到一个名为的错误;"React已经检测到InfiniteDataList调用的Hooks顺序发生了变化。">
2小时的调试——没有找到解决方案。
错误图片
Youtube教程代码——比;https://github.com/gdangelo/micro-blogging-workshop/blob/main/components/InfiniteDataList.jsYoutube教程链接——比;https://www.youtube.com/watch?v=FsngdxyvFrQ
我的代码:
InfiniteDataList.js
import React, { useEffect, useRef } from "react";
import { useInfiniteQuery } from "../../hooks";
import MessageWrapper from "../../UI/MessageWrapper";
import { isInViewport } from "../../Utility/windowUtils";
import { useDebouncedCallback } from "use-debounce";
import DefaultListItemComponent from "./components/DefaultListItemComponent";
import DefaultContainerComponent from "./components/DefaultContainerComponent";
import DefaultLoadMoreComponent from "./components/DefaultLoadMoreComponent";
const InfiniteDataList = ({
queryKey,
initialData = [],
listItemComponent: ListItemComponent = DefaultListItemComponent,
containerComponent: ContainerComponent = DefaultContainerComponent,
onError = () => {},
onEmpty = () => {},
onEmptyComponent: OnEmptyComponent = null,
onNoMoreData = () => {},
noMoreDataComponent: NoMoreDataComponent = null,
isAutoLoadMoreAtEnd = true,
autoLoadMoreAtEndOptions: {
timeout = 500,
onLoadMoreDetected = () => {},
} = {},
loadMoreComponent: LoadMoreComponent = DefaultLoadMoreComponent,
}) => {
// hooks
const {
data,
error,
hasNextPage,
fetchNextPage,
isFetchingInitialData,
isFetchingNextPageData,
} = useInfiniteQuery(queryKey, { initialData });
const moreRef = useRef();
const loadMore = useDebouncedCallback(() => {
if (isInViewport(moreRef.current)) {
onLoadMoreDetected();
fetchNextPage();
}
}, timeout);
const getLoadMoreRef = () => moreRef;
useEffect(() => {
if (isAutoLoadMoreAtEnd) {
window.addEventListener("scroll", loadMore);
}
return () => window.removeEventListener("scroll", loadMore);
}, []);
// some configuration
OnEmptyComponent = OnEmptyComponent && (() => <h4>No Details found</h4>);
NoMoreDataComponent =
NoMoreDataComponent &&
(() => <MessageWrapper message="No More Data found !" />);
// helper utils
const infiniteQueryProps = {
data,
error,
hasNextPage,
fetchNextPage,
isFetchingInitialData,
isFetchingNextPageData,
};
// if error occurs
if (error) {
onError(error);
console.log("error");
}
// no data found
if (!isFetchingInitialData && data?.length === 0) {
onEmpty();
console.log(typeof OnEmptyComponent);
return <OnEmptyComponent />;
}
// no more data to load
if (!hasNextPage) {
onNoMoreData();
}
return (
<ContainerComponent loading={isFetchingInitialData}>
{data?.map((item, index) => (
<ListItemComponent key={index} {...item} />
))}
{hasNextPage ? (
<LoadMoreComponent
{...infiniteQueryProps}
getLoadMoreRef={getLoadMoreRef}
/>
) : (
<NoMoreDataComponent {...infiniteQueryProps} />
)}
</ContainerComponent>
);
};
export default InfiniteDataList;
useInfiniteQuery.js
import useSWRInfinite from "swr/infinite";
import { axiosInstance } from "../Utility/axiosInstance";
function getFetcher(requestType = "get") {
return (url, dataToPost) =>
axiosInstance[requestType](url, dataToPost).then((res) => res.data);
}
export function useInfiniteQuery(
queryKey,
{ initialData, requestType = "get" }
) {
const { data, error, size, setSize } = useSWRInfinite(
(pageIndex, previousPageData) => {
// reached the end
if (previousPageData && !previousPageData.after) return null;
// first page
if (pageIndex === 0) return queryKey;
// next pages
const search = queryKey.includes("?");
return `${queryKey}${search ? "$" : "?"}cursor=${encodeURIComponent(
JSON.stringify(previousPageData.after)
)}`;
},
getFetcher(requestType),
initialData
);
// to fetch next page from react component
function fetchNextPage() {
setSize((prev) => prev + 1);
}
// flatten all the data obtained so far to a single array
const flattenPages = data?.flatMap((page) => page.data) ?? [];
// indicates whether the api will have data for another page
const hasNextPage = !!data?.[size - 1]?.after;
// isLoading for initial request
const isFetchingInitialData = !data && !error;
// isLoading for other requests including the initial request
const isFetchingNextPageData =
isFetchingInitialData ||
(size > 0 && data && typeof data[size - 1] === "undefined");
return {
data: flattenPages,
error,
hasNextPage,
fetchNextPage,
isFetchingInitialData,
isFetchingNextPageData,
};
}
isInViewport.js
// Check if element is visible inside the viewport
export function isInViewport(element) {
if (!element) return false;
const rect = element.getBoundingClientRect();
return (
rect.top >= 0 &&
rect.left >= 0 &&
rect.bottom <=
(window.innerHeight || document.documentElement.clientHeight) &&
rect.right <= (window.innerWidth || document.documentElement.clientWidth)
);
}
DefaultLoadMoreComponent.js
import React, { useState } from "react";
const DefaultLoadMoreComponent = ({ getLoadMoreRef = () => {} }) => {
const ref = getLoadMoreRef();
return <div ref={ref} />;
};
export default DefaultLoadMoreComponent;
DefaultListItemComponent.js
import React from "react";
const DefaultListItemComponent = ({ children = [] }) => <div>{children}</div>;
export default DefaultListItemComponent;
DefaultContainerComponent.js
import React from "react";
import AsyncDiv from "../../../UI/AsyncDiv";
const DefaultContainerComponent = ({ children = [], ...rest }) => (
<AsyncDiv {...rest}>{children}</AsyncDiv>
);
export default DefaultContainerComponent;
我渲染InfiniteDataList组件的组件
import React from "react";
import InfiniteDataList from "../../../../../UI/InfiniteDataList";
import PaginatedLeads from "./components/PaginatedLeads";
import { getError } from "../../../../../Utility/apiUtils";
const ViewAllLeads = (props) => {
return (
<InfiniteDataList
initialData={[]}
listItemComponent={PaginatedLeads}
onError={(err) =>
window.flash({ title: getError(err).message, type: "error" })
}
queryKey="/employee/leads"
/>
);
};
export default ViewAllLeads;
PaginatedLeads.js
import React from "react";
const PaginatedLeads = (props) => {
console.log(props);
return <div>PaginatedLeads</div>;
};
export default PaginatedLeads;
这是我的错。
在<<p> strong> useInfiniteQuery.js 文件中,我传递了错误格式的初始数据。语法错误——参见(initialData)最后一行
const { data, error, size, setSize } = useSWRInfinite(
(pageIndex, previousPageData) => {
// reached the end
if (previousPageData && !previousPageData.after) return null;
// first page
if (pageIndex === 0) return queryKey;
// next pages
const search = queryKey.includes("?");
return `${queryKey}${search ? "&" : "?"}cursor=${encodeURIComponent(
JSON.stringify(previousPageData.after)
)}`;
},
getFetcher(requestType),
initialData
);
正确语法——参见最后一行({fallbackData: initialData})
const { data, error, size, setSize } = useSWRInfinite(
(pageIndex, previousPageData) => {
// reached the end
if (previousPageData && !previousPageData.after) return null;
// first page
if (pageIndex === 0) return queryKey;
// next pages
const search = queryKey.includes("?");
return `${queryKey}${search ? "&" : "?"}cursor=${encodeURIComponent(
JSON.stringify(previousPageData.after)
)}`;
},
getFetcher(requestType),
{ fallbackData: initialData }
);