如何在我的 React 中停止重复数组 - 使用无限滚动?
How can I stop duplicate array in my React - using infinite scroll?
我尝试使用 React-Infinite-Scroll-Component
进行无限滚动
一切都像我想的那样完美运行,但问题是每当我更改流派,或按 F5 重新加载页面,或者不知何故,滚动条总是在最底部,我确实尝试修复它window.scrollTo(0,0)
但还是不行。
或者我弄乱了代码,因为我非常努力地解决它,但我认为我执行得不够好。
代码非常长,但这里有一个简短的解释,说明我想在其中做什么。
我收到一个 slug,它定义为 genre
和 useParams
,例如 action
或 adventure
,然后我将其设置为状态 genreAnime
,然后我检查它是否是同一类型,它会使用 concat array
进行无限加载,并将页面增加到 1 以供下一次加载,否则我将设置一个新的 array
和初始页面回到 1,否则它将继续与以前的 genre
的旧数组合并。 totalPage
设置为 91,每当它第二次渲染时,它会自动获取最新的 totalPage
并重新设置,所以可以放心使用。
translateGenre
只是把类型翻译成文字,所以请不要打扰。
但是每当我刷新 (F5) 或浏览一点时,我都会更改类型,它会得到相同 first array
的副本(由于警告,我看到它有2 个相同的项目)。由于我的滚动条总是停留在“末尾”或React-Infinite-Scroll-Component的default
值,这为0.8,表示当用户低于总高度的80%时,会触发Infinite-Scroll-Component
的next
功能
这是我的代码:
function AnimeGenre({ instance }) {
const { genre } = useParams()
const CancelToken = axios.CancelToken
const source = CancelToken.source()
const [animeList, setAnimeList] = useState([])
const [genreAnime, setGenreAnime] = useState("")
const [page, setPage] = useState(1)
const [totalPage, setTotalPage] = useState(91)
const [translateGenreAnime, setTranslateGenreAnime] = useState("")
const getList = async () => {
await instance
.get(`/anime/${genre}?page=${page}`, {
cancelToken: source.token,
})
.then((response) => {
const newPage = page + 1
setPage(newPage)
const newList = response.data.data.map((anime) => ({
slug: anime.slug,
thumbnail: anime.thumbnail,
name: anime.name,
views: anime.views,
}))
setTotalPage(response.data.pagination.totalPage)
setAnimeList((prev) => {
return [...new Set([...prev, ...newList])]
})
})
.catch((thrown) => {
if (axios.isCancel(thrown)) return
})
}
useEffect(() => {
if (genre === genreAnime) {
getList()
translateGenre()
} else {
window.onbeforeunload = function () {
window.scrollTo(0, 0)
}
window.scrollTo({
top: 0,
})
setPage(1)
setAnimeList([])
setGenreAnime(genre)
}
return () => {
source.cancel()
}
}, [genreAnime, genre])
const translateGenre = () => {
for (let i = 0; i < GENRES.length; i++) {
if (genreAnime == GENRES[i].slug) {
setTranslateGenreAnime(GENRES[i].name)
}
}
}
return (
<>
<div>
<h1>ANIME {translateGenreAnime}</h1>
</div>
<div className="anime-list">
<InfiniteScroll
initialScrollY={0}
style={{ overflow: "none" }}
dataLength={animeList.length}
next={getList}
hasMore={page === totalPage ? false : true}
loader={
<Row xs={1} sm={2} md={3} lg={4}>
{[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11].map((skeleton) => (
<Col key={skeleton}>
<Card>
<Card.Body style={{ maxHeight: "100%" }}>
<Skeleton
variant="rectangular"
width="100%"
height="192px"
animation="wave"
sx={{ bgcolor: "grey.900" }}
/>
<Skeleton
variant="text"
animation="wave"
sx={{ bgcolor: "grey.900" }}
/>
</Card.Body>
</Card>
</Col>
))}
</Row>
}
endMessage={<h4>NO MORE THINGS TO LOAD</h4>}
>
<Row xs={1} sm={2} md={3} lg={4}>
{animeList.map((anime) => (
<Col key={anime.slug}>
<Card>
<div className="card-container">
<Card.Img
variant="top"
src={anime.thumbnail}
fluid="true"
/>
<div className="overlay-card">
<a className="icon">{<BsFillPlayFill size={40} />}</a>
</div>
</div>
<Card.Body>
<Card.Title>
<TextTruncate
line={2}
element="span"
truncateText="…"
text={anime?.name}
/>
</Card.Title>
</Card.Body>
</Card>
<div className="w-100"></div>
</Col>
))}
</Row>
</InfiniteScroll>
</div>
<>
}
只要 scrollbar
保持在最顶部就可以正常工作,因为它不会超过 80% 高度的阈值。
工作之一:
当我稍微浏览并更改 GENRE
时失败,因为滚动条停留在底部,然后稍后,它将执行 scrollTo(0,0)。这不是我想要的,我想要相反的,它必须总是先到顶部...
第二次失败是当我使用 F5 按钮重新加载页面时。它会以某种方式显示非常奇怪(滚动条)。
更新:
我尝试禁用 smooth-scrolling behavior
并将滚动条设置到具有导航栏的 header
组件中,这似乎是更合适的方法,它让我的滚动条到顶部每当我在 header 中单击某些内容或使用 F5 刷新时,我都希望如此。
但是如果第一次渲染,我浏览页面和向下滚动太快,我会得到重复的错误,因为我达到了阈值并且之前的功能还在执行,如果我等待先渲染到完全加载,然后就差不多好了,有没有合适的无限滚动的方法,请告诉我一些简单的方法,我觉得我做得太复杂了。
我想我做对了,但不太确定,它工作正常,“目前”。
我就是这样做的。
而不是
const newPage = page + 1
setPage(newPage)
在 getList
函数中,我将其拆分为另一个名为 scrollThreshold
的函数
const scrollThreshold = () => {
const newPage = page + 1
setPage(newPage)
}
然后我设置
const PAGE_NUMBER = 1
在组件外部(否则我认为我应该使用 useRef
或保留它,以防万一)
然后在 useEffect
,我又实现了 1 个依赖项,它是 page
状态,每当我得到新的 page
或 page
状态变化时,或者 genre
改变,它将再次 运行 useEffect
。
然后在 InfiniteScroll
组件中,我将其从
稍作更改
<InfiniteScroll>
...
next={getList}
...
</InfiniteScroll>
进入
<InfiniteScroll>
...
next={getList && scrollThreshold}
...
</InfiniteScroll>
所以每当我向下滚动时,它都会触发 getList 和 scrollThreshold,通过分开,它不会像我的第一个代码那样聚集在一起。目前还可以,不知道好不好。
我尝试使用 React-Infinite-Scroll-Component
进行无限滚动一切都像我想的那样完美运行,但问题是每当我更改流派,或按 F5 重新加载页面,或者不知何故,滚动条总是在最底部,我确实尝试修复它window.scrollTo(0,0)
但还是不行。
或者我弄乱了代码,因为我非常努力地解决它,但我认为我执行得不够好。
代码非常长,但这里有一个简短的解释,说明我想在其中做什么。
我收到一个 slug,它定义为 genre
和 useParams
,例如 action
或 adventure
,然后我将其设置为状态 genreAnime
,然后我检查它是否是同一类型,它会使用 concat array
进行无限加载,并将页面增加到 1 以供下一次加载,否则我将设置一个新的 array
和初始页面回到 1,否则它将继续与以前的 genre
的旧数组合并。 totalPage
设置为 91,每当它第二次渲染时,它会自动获取最新的 totalPage
并重新设置,所以可以放心使用。
translateGenre
只是把类型翻译成文字,所以请不要打扰。
但是每当我刷新 (F5) 或浏览一点时,我都会更改类型,它会得到相同 first array
的副本(由于警告,我看到它有2 个相同的项目)。由于我的滚动条总是停留在“末尾”或React-Infinite-Scroll-Component的default
值,这为0.8,表示当用户低于总高度的80%时,会触发Infinite-Scroll-Component
next
功能
这是我的代码:
function AnimeGenre({ instance }) {
const { genre } = useParams()
const CancelToken = axios.CancelToken
const source = CancelToken.source()
const [animeList, setAnimeList] = useState([])
const [genreAnime, setGenreAnime] = useState("")
const [page, setPage] = useState(1)
const [totalPage, setTotalPage] = useState(91)
const [translateGenreAnime, setTranslateGenreAnime] = useState("")
const getList = async () => {
await instance
.get(`/anime/${genre}?page=${page}`, {
cancelToken: source.token,
})
.then((response) => {
const newPage = page + 1
setPage(newPage)
const newList = response.data.data.map((anime) => ({
slug: anime.slug,
thumbnail: anime.thumbnail,
name: anime.name,
views: anime.views,
}))
setTotalPage(response.data.pagination.totalPage)
setAnimeList((prev) => {
return [...new Set([...prev, ...newList])]
})
})
.catch((thrown) => {
if (axios.isCancel(thrown)) return
})
}
useEffect(() => {
if (genre === genreAnime) {
getList()
translateGenre()
} else {
window.onbeforeunload = function () {
window.scrollTo(0, 0)
}
window.scrollTo({
top: 0,
})
setPage(1)
setAnimeList([])
setGenreAnime(genre)
}
return () => {
source.cancel()
}
}, [genreAnime, genre])
const translateGenre = () => {
for (let i = 0; i < GENRES.length; i++) {
if (genreAnime == GENRES[i].slug) {
setTranslateGenreAnime(GENRES[i].name)
}
}
}
return (
<>
<div>
<h1>ANIME {translateGenreAnime}</h1>
</div>
<div className="anime-list">
<InfiniteScroll
initialScrollY={0}
style={{ overflow: "none" }}
dataLength={animeList.length}
next={getList}
hasMore={page === totalPage ? false : true}
loader={
<Row xs={1} sm={2} md={3} lg={4}>
{[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11].map((skeleton) => (
<Col key={skeleton}>
<Card>
<Card.Body style={{ maxHeight: "100%" }}>
<Skeleton
variant="rectangular"
width="100%"
height="192px"
animation="wave"
sx={{ bgcolor: "grey.900" }}
/>
<Skeleton
variant="text"
animation="wave"
sx={{ bgcolor: "grey.900" }}
/>
</Card.Body>
</Card>
</Col>
))}
</Row>
}
endMessage={<h4>NO MORE THINGS TO LOAD</h4>}
>
<Row xs={1} sm={2} md={3} lg={4}>
{animeList.map((anime) => (
<Col key={anime.slug}>
<Card>
<div className="card-container">
<Card.Img
variant="top"
src={anime.thumbnail}
fluid="true"
/>
<div className="overlay-card">
<a className="icon">{<BsFillPlayFill size={40} />}</a>
</div>
</div>
<Card.Body>
<Card.Title>
<TextTruncate
line={2}
element="span"
truncateText="…"
text={anime?.name}
/>
</Card.Title>
</Card.Body>
</Card>
<div className="w-100"></div>
</Col>
))}
</Row>
</InfiniteScroll>
</div>
<>
}
只要 scrollbar
保持在最顶部就可以正常工作,因为它不会超过 80% 高度的阈值。
工作之一:
当我稍微浏览并更改 GENRE
时失败,因为滚动条停留在底部,然后稍后,它将执行 scrollTo(0,0)。这不是我想要的,我想要相反的,它必须总是先到顶部...
第二次失败是当我使用 F5 按钮重新加载页面时。它会以某种方式显示非常奇怪(滚动条)。
更新:
我尝试禁用 smooth-scrolling behavior
并将滚动条设置到具有导航栏的 header
组件中,这似乎是更合适的方法,它让我的滚动条到顶部每当我在 header 中单击某些内容或使用 F5 刷新时,我都希望如此。
但是如果第一次渲染,我浏览页面和向下滚动太快,我会得到重复的错误,因为我达到了阈值并且之前的功能还在执行,如果我等待先渲染到完全加载,然后就差不多好了,有没有合适的无限滚动的方法,请告诉我一些简单的方法,我觉得我做得太复杂了。
我想我做对了,但不太确定,它工作正常,“目前”。
我就是这样做的。
而不是
const newPage = page + 1
setPage(newPage)
在 getList
函数中,我将其拆分为另一个名为 scrollThreshold
const scrollThreshold = () => {
const newPage = page + 1
setPage(newPage)
}
然后我设置
const PAGE_NUMBER = 1
在组件外部(否则我认为我应该使用 useRef
或保留它,以防万一)
然后在 useEffect
,我又实现了 1 个依赖项,它是 page
状态,每当我得到新的 page
或 page
状态变化时,或者 genre
改变,它将再次 运行 useEffect
。
然后在 InfiniteScroll
组件中,我将其从
<InfiniteScroll>
...
next={getList}
...
</InfiniteScroll>
进入
<InfiniteScroll>
...
next={getList && scrollThreshold}
...
</InfiniteScroll>
所以每当我向下滚动时,它都会触发 getList 和 scrollThreshold,通过分开,它不会像我的第一个代码那样聚集在一起。目前还可以,不知道好不好。