现在,我已经创建了这个自定义钩子来执行惰性加载,它将redux切片操作作为输入,
import { useState, useEffect, useCallback, useRef } from "react";
import { useDispatch } from "react-redux";
function useLazyFetch(fetchAction) {
const dispatch = useDispatch();
const [page, setPage] = useState(1);
const [loading, setLoading] = useState(false);
const loadMoreRef = useRef(null);
const handleObserver = useCallback(async(entries) => {
const [target] = entries;
console.log(target.isIntersecting);
if (target.isIntersecting) {
console.log("INTERSECTING.....");
await new Promise((r) => setTimeout(r, 2000));
setPage((prev) => prev + 1);
}
}, []);
useEffect(() => {
const option = {
root: null,
rootMargin: "0px",
threshold: 1.0,
};
const observer = new IntersectionObserver(handleObserver, option);
if (loadMoreRef.current) observer.observe(loadMoreRef.current);
}, [handleObserver]);
const fetchApi = useCallback(async () => {
try {
setLoading(true);
await new Promise((r) => setTimeout(r, 2000));
dispatch(fetchAction(page))
setLoading(false);
} catch (err) {
console.error(err);
}
}, [page,fetchAction,dispatch]);
useEffect(() => {
fetchApi();
}, [fetchApi]);
return { loading, loadMoreRef };
}
export default useLazyFetch;
我在我的组件中这样使用它,在这里你可以看到我在底部使用来自useLazyFetch的loadMoreRef跟踪div,现在当我注解掉**fetchApi();**从自定义挂钩它的工作如预期,滚动其日志记录INTERSECTING...在控制台中,但当我试图通过fetchApi()执行该操作时,我的整个应用程序进入循环,div跟踪器与ref来到顶部,它获取帖子,但在该操作立即重复后,跟踪器来到顶部,页面变为空&它获取下一组帖子,我可以看到我的列表在redux开发工具中获得了附加到状态的新帖子集,而不是完全设置新状态,但在UI中,它一次又一次地呈现所有帖子,这导致了循环,我如何才能避免这种情况?
import { CircularProgress, Grid, IconButton, Typography } from "@mui/material";
import { Box } from "@mui/system";
import React, { useEffect,useRef,useState } from "react";
import AssistantIcon from "@mui/icons-material/Assistant";
import Post from "../components/Post";
import { useDispatch, useSelector } from "react-redux";
import { getPosts } from "../redux/postSlice";
import AddPost from "../components/AddPost";
import useLazyFetch from "../hooks/useLazyFetch";
export default function Home() {
const dispatch = useDispatch();
// const api = `https://picsum.photos/v2/list`
const { status, posts } = useSelector((state) => state.post);
const {loading,loadMoreRef} = useLazyFetch(getPosts)
useEffect(() => {
dispatch(getPosts());
}, []);
return (
<Box>
<Box borderBottom="1px solid #ccc" padding="8px 20px">
<Grid container justifyContent="space-between" alignItems="center">
<Grid item>
<Typography variant="h6">Home</Typography>
</Grid>
<Grid item>
<IconButton>
<AssistantIcon />
</IconButton>
</Grid>
</Grid>
</Box>
<Box height="92vh" sx={{ overflowY: "scroll" }}>
<AddPost />
<Box textAlign="center" marginTop="1rem">
{status === "loading" && (
<CircularProgress size={20} color="primary" />
)}
</Box>
{status === "success" &&
posts?.map((post) => <Post key={post._id} post={post} />)}
<div style={{height:"50px",width:"100px",backgroundColor:"red"}} ref={loadMoreRef}>{loading && <p>loading...</p>}</div>
</Box>
</Box>
);
}
这是我的redux操作和状态更新部分
const initialState = {
status: "idle",
posts: []
};
export const getPosts = createAsyncThunk("post/getPosts", async (page) => {
console.log(page);
console.log("calling api ...");
const { data } = await axios.get(`/api/posts?page=${page}`);
return data;
});
export const postSlice = createSlice({
name: "post",
initialState,
reducers: {},
extraReducers: {
[getPosts.pending]: (state, action) => {
state.status = "loading";
},
[getPosts.fulfilled]: (state, action) => {
state.status = "success";
state.posts = [...state.posts,...action.payload.response.posts] ;
},
[getPosts.rejected]: (state, action) => {
state.status = "failed";
},
}
1条答案
按热度按时间sqserrrh1#
这是有效的解决方案
我用useMemo钩子来记忆,它的工作和预期的一样