Issue
I am struggling to find a pattern that works for an infinite scroll component I am working on for reference I am using the react-intersection-observer
hook. The problem is that my fetchData
function is being called twice when the inView
state is true. I have tried quite a few solutions and none of them seem to work. The flow seems to be:
- Page loads
inView
is false. - User scrolls
inView
becomes true. fetchData
is asynchronously called and useEffect ends.res
is finally returned andsetQueryData
finally updates the state.- This triggers the
useEffect
to execute again but since the data has just been set, it hasn't been rendered yet and pushed theinView
linked component down out of the way. - Since
inView
is stilltrue
anotherfetchData
is executed.
It seems like I need to make inView
false immediately after the useEffect
is run but not sure if this possible or correct, any advice would be greatly appreciated!
type LoadMoreState = {
data: QueryDatabaseResponse[];
nextCursor: string | undefined;
};
export function LoadMore({ cursor }: { cursor: string }) {
const { ref, inView } = useInView();
const [queryData, setQueryData] = useState<LoadMoreState>({
data: [],
nextCursor: cursor,
});
useEffect(() => {
const fetchData = async () => {
if (inView) {
try {
const res = await getGoodFirstIssues({
page_size: 10,
start_cursor: queryData.nextCursor,
});
setQueryData((prevData) => ({
data: prevData.data.concat(res),
nextCursor: res.next_cursor || undefined,
}));
} catch (error) {
console.log("There was an error fetching the data", error);
}
}
};
fetchData();
}, [inView, queryData]);
Solution
The problem is that you need queryData
inside the useEffect
, because of the nextCursor
, but it also triggers the useEffect
, while inView
is still true
.
Store the next_cursor
in a ref (nextCursorRef
), and remove it from the dependencies of the useEffect
:
export function LoadMore({ cursor }: { cursor: string }) {
const nextCursorRef = useRef<string | undefined>();
const { ref, inView } = useInView();
const [queryData, setQueryData] = useState<QueryDatabaseResponse[]>([]);
useEffect(() => {
const fetchData = async () => {
if (inView) {
try {
const res = await getGoodFirstIssues({
page_size: 10,
start_cursor: nextCursorRef.current,
});
nextCursorRef.current = res.next_cursor || undefined;
setQueryData((prevData) => prevData.concat(res));
} catch (error) {
console.log("There was an error fetching the data", error);
}
}
};
fetchData();
}, [inView]);
I would also refactor this a bit more, to prevent defining and calling fetchData
if inView
is false
:
export function LoadMore({ cursor }: { cursor: string }) {
const nextCursorRef = useRef<string | undefined>();
const { ref, inView } = useInView();
const [queryData, setQueryData] = useState<QueryDatabaseResponse[]>([]);
useEffect(() => {
if (!inView) return undefined; // if not in view no need to define and call fetchData
const fetchData = async () => {
try {
const res = await getGoodFirstIssues({
page_size: 10,
start_cursor: nextCursorRef.current,
});
nextCursorRef.current = res.next_cursor || undefined;
setQueryData((prevData) => prevData.concat(res));
} catch (error) {
console.log("There was an error fetching the data", error);
}
};
fetchData();
}, [inView]);
Answered By - Ori Drori
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.