英文:
Data is not updated correctly after page changes in Pagination Component
问题
以下是代码的翻译:
分页组件:
import React from 'react';
import classnames from 'classnames';
import { usePagination, DOTS } from './usePagination';
import './pagination.css';
const Pagination = props => {
const {
onPageChange,
totalCount,
siblingCount = 1,
currentPage,
pageSize,
className
} = props;
const paginationRange = usePagination({
currentPage,
totalCount,
siblingCount,
pageSize
});
if (currentPage === 0 || paginationRange.length < 2) {
return null;
}
const onNext = () => {
onPageChange(currentPage + 1);
};
const onPrevious = () => {
onPageChange(currentPage - 1);
};
let lastPage = paginationRange[paginationRange.length - 1];
return (
<ul
className={classnames('pagination-container', { [className]: className })}
>
<li
className={classnames('pagination-item', {
disabled: currentPage === 1
})}
onClick={onPrevious}
>
<div className="arrow left" />
</li>
{paginationRange.map(pageNumber => {
if (pageNumber === DOTS) {
return <li className="pagination-item dots">...</li>;
}
return (
<li
className={classnames('pagination-item', {
selected: pageNumber === currentPage
})}
onClick={() => onPageChange(pageNumber)}
>
{pageNumber}
</li>
);
})}
<li
className={classnames('pagination-item', {
disabled: currentPage === lastPage
})}
onClick={onNext}
>
<div className="arrow right" />
</li>
</ul>
);
};
export default Pagination;
评论页面中的代码:
import React, { useEffect, useState, useMemo } from "react";
import { useDispatch, useSelector } from "react-redux";
import LoadingSpinner from "../Commons/LoadingSpinner";
import FeedCheckFilter from "../Commons/Filters/Filter";
import { getLimitedReviews, updateFilters } from '../../services/FiltersService';
import Review from './Review';
import Pagination from "../Commons/Pagination/Pagination";
let PageSize = 10;
const initialState = {
limit: 10,
offset: 0,
reviews: [],
count: 0
}
export default function Reviews() {
const dispatch = useDispatch()
const token = useSelector((state) => state.user.profile.token)
const username = useSelector((state) => state.user.profile.auth)
const reviews = useSelector((state) => state.reviewsData.reviews)
const [currentPage, setCurrentPage] = useState(1);
const [reviewsState, setReviewsState] = useState(initialState)
// ...
const loadData = async (queryUrl = filters.url, limit=10, offset=0) => {
setIsLoading(true)
// let queryUrl = filters.url;
if (limit || offset) {
let action = await getLimitedReviews(token, username, queryUrl || defaultQueryUrl, limit, offset)
setReviewsState(action.payload)
dispatch(action)
}
setIsLoading(false)
}
const currentReviewsData = useMemo(() => {
const firstPageIndex = (currentPage - 1) * PageSize;
const lastPageIndex = firstPageIndex + PageSize;
loadData(filters.url, lastPageIndex, firstPageIndex)
if (reviews) {
return reviews
} else {
return []
}
}, [currentPage]);
// ...
useEffect(() => {
loadData();
}, [])
// ...
return (
<div>
<h1>Reviews</h1>
{isLoading ? <LoadingSpinner /> :
<>
// ...
{currentReviewsData.map(item => {
return (<Review key={item.id} review={item} />)
})
<Pagination
className="pagination-bar"
currentPage={currentPage}
totalCount={reviewsState.count}
pageSize={PageSize}
onPageChange={page => setCurrentPage(page)}
/>
</>
}
</div>
)
}
用于分页的钩子函数:
import React from 'react';
import { useMemo } from 'react';
export const DOTS = '...';
const range = (start, end) => {
let length = end - start + 1;
return Array.from({ length }, (_, idx) => idx + start);
};
export const usePagination = ({
totalCount,
pageSize,
siblingCount = 1,
currentPage
}) => {
const paginationRange = useMemo(() => {
const totalPageCount = Math.ceil(totalCount / pageSize);
// Pages count is determined as siblingCount + firstPage + lastPage + currentPage + 2*DOTS
const totalPageNumbers = siblingCount + 5;
/*
If the number of pages is less than the page numbers we want to show in our
paginationComponent, we return the range [1..totalPageCount]
*/
if (totalPageNumbers >= totalPageCount) {
return range(1, totalPageCount);
}
const leftSiblingIndex = Math.max(currentPage - siblingCount, 1);
const rightSiblingIndex = Math.min(
currentPage + siblingCount,
totalPageCount
);
/*
We do not want to show dots if there is only one position left
after/before the left/right page count as that would lead to a change if our Pagination
component size which we do not want
*/
const shouldShowLeftDots = leftSiblingIndex > 2;
const shouldShowRightDots = rightSiblingIndex < totalPageCount - 2;
const firstPageIndex = 1;
const lastPageIndex = totalPageCount;
if (!shouldShowLeftDots && shouldShowRightDots) {
let leftItemCount = 3 + 2 * siblingCount;
let leftRange = range(1, leftItemCount);
return [...leftRange, DOTS, totalPageCount];
}
if (shouldShowLeftDots && !shouldShowRightDots) {
let rightItemCount = 3 + 2 * siblingCount;
let rightRange = range(
totalPageCount - rightItemCount + 1,
totalPageCount
);
return [firstPageIndex, DOTS, ...rightRange];
}
if (shouldShowLeftDots && shouldShowRightDots) {
let middleRange = range(leftSiblingIndex, rightSiblingIndex);
return [firstPageIndex, DOTS, ...middleRange, DOTS, lastPageIndex];
}
}, [totalCount, pageSize, siblingCount, currentPage]);
return paginationRange;
};
以上是你提供的代码的翻译部分,不包含其他内容。
英文:
Here I have the pagination component:
import React from 'react';
import classnames from 'classnames';
import { usePagination, DOTS } from './usePagination';
import './pagination.css';
const Pagination = props => {
const {
onPageChange,
totalCount,
siblingCount = 1,
currentPage,
pageSize,
className
} = props;
const paginationRange = usePagination({
currentPage,
totalCount,
siblingCount,
pageSize
});
if (currentPage === 0 || paginationRange.length < 2) {
return null;
}
const onNext = () => {
onPageChange(currentPage + 1);
};
const onPrevious = () => {
onPageChange(currentPage - 1);
};
let lastPage = paginationRange[paginationRange.length - 1];
return (
<ul
className={classnames('pagination-container', { [className]: className })}
>
<li
className={classnames('pagination-item', {
disabled: currentPage === 1
})}
onClick={onPrevious}
>
<div className="arrow left" />
</li>
{paginationRange.map(pageNumber => {
if (pageNumber === DOTS) {
return <li className="pagination-item dots">&#8230;</li>;
}
return (
<li
className={classnames('pagination-item', {
selected: pageNumber === currentPage
})}
onClick={() => onPageChange(pageNumber)}
>
{pageNumber}
</li>
);
})}
<li
className={classnames('pagination-item', {
disabled: currentPage === lastPage
})}
onClick={onNext}
>
<div className="arrow right" />
</li>
</ul>
);
};
export default Pagination;
And it is integrated in review's page like that:
import React, { useEffect, useState, useMemo } from "react";
import { useDispatch, useSelector } from "react-redux";
import LoadingSpinner from "../Commons/LoadingSpinner";
import FeedCheckFilter from "../Commons/Filters/Filter";
import { getLimitedReviews, updateFilters } from '../../services/FiltersService';
import Review from './Review';
import Pagination from "../Commons/Pagination/Pagination";
let PageSize = 10;
const initialState = {
limit: 10,
offset: 0,
reviews: [],
count: 0
}
export default function Reviews() {
const dispatch = useDispatch()
const token = useSelector((state) => state.user.profile.token)
const username = useSelector((state) => state.user.profile.auth)
const reviews = useSelector((state) => state.reviewsData.reviews)
const [currentPage, setCurrentPage] = useState(1);
const [reviewsState, setReviewsState] = useState(initialState)
...
const loadData = async (queryUrl = filters.url, limit=10, offset=0) => {
setIsLoading(true)
// let queryUrl = filters.url;
if (limit || offset) {
let action = await getLimitedReviews(token, username, queryUrl || defaultQueryUrl, limit, offset)
setReviewsState(action.payload)
dispatch(action)
}
setIsLoading(false)
}
const currentReviewsData = useMemo(() => {
const firstPageIndex = (currentPage - 1) * PageSize;
const lastPageIndex = firstPageIndex + PageSize;
loadData(filters.url, lastPageIndex, firstPageIndex)
if (reviews) {
return reviews
} else {
return []
}
}, [currentPage]);
...
useEffect(() => {
loadData();
}, [])
...
return (
<div>
<h1>Reviews</h1>
{isLoading ? <LoadingSpinner /> :
<>
...
{currentReviewsData.map(item => {
return (<Review key={item.id} review={item} />)
})}
<Pagination
className="pagination-bar"
currentPage={currentPage}
totalCount={reviewsState.count}
pageSize={PageSize}
onPageChange={page => setCurrentPage(page)}
/>
</>
}
</div>
)
}
The problem is that when I press the button to go from page 1 to page 2, the reviews are updated only after render. So the page remains with the reviews from the previous page.
For example if I am in the 5th page, i press the button to go on the 6th. Here on the 6th page I see the reviews from 5th and now if I go to page 7 for example, then I will see the reviews from page 6.
Here I have a hook that I'm using for pagination but I think the problem is not from here:
import React from 'react';
import { useMemo } from 'react';
export const DOTS = '...';
const range = (start, end) => {
let length = end - start + 1;
return Array.from({ length }, (_, idx) => idx + start);
};
export const usePagination = ({
totalCount,
pageSize,
siblingCount = 1,
currentPage
}) => {
const paginationRange = useMemo(() => {
const totalPageCount = Math.ceil(totalCount / pageSize);
// Pages count is determined as siblingCount + firstPage + lastPage + currentPage + 2*DOTS
const totalPageNumbers = siblingCount + 5;
/*
If the number of pages is less than the page numbers we want to show in our
paginationComponent, we return the range [1..totalPageCount]
*/
if (totalPageNumbers >= totalPageCount) {
return range(1, totalPageCount);
}
const leftSiblingIndex = Math.max(currentPage - siblingCount, 1);
const rightSiblingIndex = Math.min(
currentPage + siblingCount,
totalPageCount
);
/*
We do not want to show dots if there is only one position left
after/before the left/right page count as that would lead to a change if our Pagination
component size which we do not want
*/
const shouldShowLeftDots = leftSiblingIndex > 2;
const shouldShowRightDots = rightSiblingIndex < totalPageCount - 2;
const firstPageIndex = 1;
const lastPageIndex = totalPageCount;
if (!shouldShowLeftDots && shouldShowRightDots) {
let leftItemCount = 3 + 2 * siblingCount;
let leftRange = range(1, leftItemCount);
return [...leftRange, DOTS, totalPageCount];
}
if (shouldShowLeftDots && !shouldShowRightDots) {
let rightItemCount = 3 + 2 * siblingCount;
let rightRange = range(
totalPageCount - rightItemCount + 1,
totalPageCount
);
return [firstPageIndex, DOTS, ...rightRange];
}
if (shouldShowLeftDots && shouldShowRightDots) {
let middleRange = range(leftSiblingIndex, rightSiblingIndex);
return [firstPageIndex, DOTS, ...middleRange, DOTS, lastPageIndex];
}
}, [totalCount, pageSize, siblingCount, currentPage]);
return paginationRange;
};
答案1
得分: 0
I think your memo isn't waiting for the result so you always get the result of the previous page loadData
is an asynchronous function.
const currentReviewsData = useMemo(() => {
const firstPageIndex = (currentPage - 1) * PageSize;
const lastPageIndex = firstPageIndex + PageSize;
loadData(filters.url, lastPageIndex, firstPageIndex)
if (reviews) {
return reviews
} else {
return []
}
}, [currentPage]);
In my opinion, I don't think you should use a memo for currentReviewsData
.
Try this:
const [currentReviewsData, setCurrentReviewsData] = useState([]);
useEffect(() => {
if (typeof currentPage !== "undefined") {
const firstPageIndex = (currentPage - 1) * PageSize;
const lastPageIndex = firstPageIndex + PageSize;
loadData(filters.url, lastPageIndex, firstPageIndex).then((reviews) => {
setCurrentReviewsData(reviews);
});
}
}, [currentPage]);
英文:
I think your memo isn't waiting for the result so you always get the result of the previous page loadData
is an asynchronous function.
const currentReviewsData = useMemo(() => {
const firstPageIndex = (currentPage - 1) * PageSize;
const lastPageIndex = firstPageIndex + PageSize;
loadData(filters.url, lastPageIndex, firstPageIndex)
if (reviews) {
return reviews
} else {
return []
}
}, [currentPage]);
In my opinion I don't think you should use a memo for currentReviewsData
.
Try this:
const [currentReviewsData, setCurrentReviewsData] = useState([]);
useEffect(() => {
if (typeof currentPage !== "undefined") {
const firstPageIndex = (currentPage - 1) * PageSize;
const lastPageIndex = firstPageIndex + PageSize;
loadData(filters.url, lastPageIndex, firstPageIndex).then((reviews) => {
setCurrentReviewsData(reviews);
});
}
}, [currentPage]);
</details>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论