英文:
Updating results per page only working on first page of pagination-react
问题
我可以让我的分页正常工作,但是当我想让用户更改每页显示的项目数量时,它只对第一页进行分页,并在第二页显示剩余的结果。
我还不断收到一个错误,说我的 pageCount 属性不是整数,需要使用 Math.ceil()。但我不确定在哪里使用它。
// pagination
// 设置为0,因为如果设置为1,它不会显示所有数据
const [currentPage, setCurrentPage] = useState(0);
const [productPerPage, setProductPerPage] = useState(30);
// const [itemOffset, setItemOffset] = useState(0);
const pageCount = Math.ceil(products.length / productPerPage);
const handleChanges = (e) => {
setProductPerPage(e.target.value);
console.log(pageCount);
console.log(productPerPage);
}
const pagesVisited = currentPage * productPerPage;
在你的代码中,你已经正确地使用了 Math.ceil()
来计算 pageCount
,这是为了确保页数是整数。其他部分似乎也没有问题,但如果你仍然遇到问题,可能需要进一步检查其他与分页相关的部分的实现,以确保它们与期望的行为一致。希望这可以帮助你解决问题!
英文:
I am able to make my pagination work as it should, however when I want the user to change the amount of items they see each page, it only paginates the first page and shows the remaining of the results on the second page.
I also keep getting an error that my pageCount prop is not an integer and I need to use Math.ciel().However I'm confused as where I need to use it.
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
import { useContext, useState } from 'react';
import Searchbar from '../Components/Searchbar';
import OptionButtons from '../Components/OptionButtons';
import { AppContext } from '../Context/Context';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faBasketShopping } from '@fortawesome/free-solid-svg-icons';
import { Link } from 'react-router-dom';
import useAxios from '../Hooks/useAxios';
import savedHook from '../Hooks/savedHook';
import PriceSlider from '../Components/PriceSlider';
import BrandList from '../Components/BrandList';
import Pagination from 'react-paginate'
import Drawer from 'react-modern-drawer'
import Accordion from '../Components/Accordion';
import 'react-modern-drawer/dist/index.css'
import '../Assets/Styles/Shop.css'
function Shop() {
// States //////////////////////////////////////////////////////////////
const { products, setProducts, isLoading, serverErr, getProductsByBrand,
getProductsByType, selectAProduct, error, filterProduct, priceRangeProducts, openFilterDrawer, filterDrawer } = useAxios('https://makeup-api.herokuapp.com/api/v1/products.json')
// saved icon to shaded
const { likedIndex, changeIcon } = savedHook()
const accordionData = [
{
'id': 1,
'heading': 'Price Range',
'content': <PriceSlider onSlider={priceRangeProducts} />
},
{
'id': 2,
'heading': 'Brand',
'content': <BrandList brandDropDown={getProductsByBrand} />
}
]
// useContext for the add to cart
const Cartstate = useContext(AppContext)
const dispatch = Cartstate.dispatch;
// Usecontext for the saved array as it uses a different function
const Savestate = useContext(AppContext)
const saveDispatch = Savestate.saveDispatch
// pagination
// set to 0 because if I set it to 1 then it doesn't show all the data
const [currentPage, setCurrentPage] = useState(0)
const [productPerPage, setProductPerPage] = useState(30)
// const [itemOffset, setItemOffset] = useState(0);
const pageCount = Math.ceil(products.length / productPerPage);
const handleChanges = (e) => {
setProductPerPage(e.target.value)
console.log(pageCount)
console.log(productPerPage)
}
const pagesVisited = currentPage * productPerPage
// How I want the data to be shown on the page
const displayProducts = products.slice(
pagesVisited, pagesVisited + productPerPage
)
.map((item, index) => {
return (
<div className='singleCard h-full flex flex-col mx-1 transition ease-in-out delay-150 hover:-translate-y-1 hover:scale-100 hover: duration-300 hover:shadow-lg' key={item.id}>
< button className='mx-1 my-1' value={item.brand + item.product_type} onClick={() => { changeIcon(index); saveDispatch({ type: 'SAVE', saveIt: item }) }}>
{likedIndex[index] ?
(
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" className="redHeart w-6 h-6">
<path d="M11.645 20.91l-.007-.003-.022-.012a15.247 15.247 0 01-.383-.218 25.18 25.18 0 01-4.244-3.17C4.688 15.36 2.25 12.174 2.25 8.25 2.25 5.322 4.714 3 7.688 3A5.5 5.5 0 0112 5.052 5.5 5.5 0 0116.313 3c2.973 0 5.437 2.322 5.437 5.25 0 3.925-2.438 7.111-4.739 9.256a25.175 25.175 0 01-4.244 3.17 15.247 15.247 0 01-.383.219l-.022.012-.007.004-.003.001a.752.752 0 01-.704 0l-.003-.001z" />
</svg>
)
: (
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth="1.5" stroke="currentColor" className="w-6 h-6" >
<path strokeLinecap="round" strokeLinejoin="round" d="M21 8.25c0-2.485-2.099-4.5-4.688-4.5-1.935 0-3.597 1.126-4.312 2.733-.715-1.607-2.377-2.733-4.313-2.733C5.1 3.75 3 5.765 3 8.25c0 7.22 9 12 9 12s9-4.78 9-12z" />
</svg>
)
}
</button>
<Link to={`/product/${item.id}`} name={item.brand}>
<img className='productImg ml-8 mb-2 md:ml-8 xl:ml-14' src={item.api_featured_image} alt={item.brand + item.product_type}></img>
{/* To display the brand name with as sentence case */}
<div className='productText px-2 flex flex-col content-end md:px-1 '>
<p className='productType'>{item?.product_type ? item.product_type.charAt(0) + item.product_type.slice(1).toLowerCase().split('_').join(' ') : item.product_type}</p>
<p className='productBrand'>
{item?.brand ? item.brand.charAt(0).toUpperCase() + item.brand.slice(1).toLowerCase() : item.brand} </p>
<p className='productName'>{item?.name ? item.name.charAt(0).toUpperCase() + item.name.slice(1).toLowerCase() : item.name}</p>
</div>
</Link>
<div className='flex flex-row mt-auto mx-2 my-2'>
<div className='mr-auto mt-auto'>
<p className='productPriceShop'><span className='circleShadow '>£{Number(item.price).toFixed(2)}</span>
</p>
</div>
<div className='addToCart ml-auto mt-auto '>
<button className='basketProduct' onClick={() => dispatch({ type: 'ADD', payload: item })}>
<FontAwesomeIcon icon={faBasketShopping} />
</button>
</div>
</div>
</div>
)
})
// calucates total pages need for all the data with how many items to be shown each page
// Math.ceil rounds the number of pages to a whole integer
//callback function invoked with the updated page value when the page is changed.
const changePage = ({ selected }) => {
setCurrentPage(selected)
console.log(pagesVisited)
console.log(selected)
}
const sortThis = (e) => {
const sorting = e.target.value
const productList = [...products]
const prices = productList.sort((a, b) => {
return sorting === 'asc' ? a.price - b.price : b.price - a.price
});
setProducts(prices)
}
return (
<>
<div className='shopTopBanner'>
<h1>Cosmetics for you</h1>
<ol className='routeProductPage flex flex-row mr-2'>
<li><Link to='/'>Home/</Link></li>
<li><Link to='/shop'>Shop</Link></li>
</ol>
</div>
<div className='shopColumn'>
<div className='leftShopColumn'>
<div className='searchBar md:hidden'>
<Searchbar
onSearch={getProductsByBrand}
onFilter={getProductsByType}
onInput={filterProduct}
/>
</div>
<div className='priceSlider hidden md:inline-block'>
<PriceSlider
onSlider={priceRangeProducts}></PriceSlider>
</div>
<div className='productTypeButtons'>
<OptionButtons onButton={selectAProduct} />
</div>
<div className='mobileSortMenu flex flex-row md:hidden'>
<div>
<button onClick={openFilterDrawer}>
More Options
</button>
</div>
<div className='productPerPageMobile'>
<select
value={productPerPage}
onChange={handleChanges}>
<option defaultValue>Products per page</option>
<option value={5}>
5 products
</option>
<option value={10}>
10 products
</option>
<option value={20}>
20 products
</option>
</select>
</div>
<div className='sortingDropdownMobile'>
<select onChange={sortThis}>Sort it out
<option defaultValue>Sort</option>
<option value={'asc'}>Ascending</option>
<option value={'desc'}>Descending</option>
</select>
</div>
</div>
{filterDrawer ?
<>
<Drawer
open={filterDrawer}
onClose={openFilterDrawer}
direction='bottom'>
<div className='flex flex-row'>
<h1>Filter and Sort </h1>
<h1 onClick={openFilterDrawer}>Close</h1>
</div>
<div className='flex flex-col'>
<ul className="accordion">
{accordionData.map(({ heading, content }) => (
<Accordion heading={heading} content={content} />
))}
</ul>
</div>
</Drawer>
</>
: null}
<div className='listOfBrands hidden md:inline-block'>
<BrandList brandDropDown={getProductsByBrand}></BrandList>
</div>
<div className='sortingDropdown hidden md:inline-block'>
<select onChange={sortThis}>Sort it out
<option defaultValue>Sort</option>
<option value={'asc'}>Ascending</option>
<option value={'desc'}>Descending</option>
</select>
</div>
<div className='productsPerPage hidden md:inline-block'>
<select
value={productPerPage}
onChange={handleChanges}>
<option defaultValue>Products per page</option>
<option value={5}>
5 products
</option>
<option value={10}>
10 products
</option>
<option value={20}>
20 products
</option>
</select>
</div>
<button>
Reset filters
</button>
</div>
<div className='rightShopColumn'>
<div className='searchBar hidden md:inline-block'>
<Searchbar
onSearch={getProductsByBrand}
onFilter={getProductsByType}
onInput={filterProduct}
/>
</div>
{serverErr && <div>{serverErr}</div>}
{error ? <div>{error}</div>:
<div>
{!isLoading ? <>
{products.length ?
<>
<div className='shopCards grid grid-cols-2 flex-wrap py-4 mr-1 md:grid-cols-4 lg:grid-cols-4'>
{displayProducts}
</div>
<div className='flex flex-col'>
<div >
<h1> Showing {pagesVisited===0?1:pagesVisited} - {pagesVisited===0?30: pagesVisited*2} of {products.length} products</h1>
</div>
<div>
<Pagination
previousLabel={'Previous page'}
// onChange={handleChanges}
nextLabel={'Next page'}
pageCount={pageCount}
pageClassName='pageNoneDisplay'
breakClassName='pageNoneDisplay'
onPageChange={changePage}
containerClassName={'paginationBtns py-3'}
previousLinkClassName={'previousBtn'}
nextLinkClassName={'nextBtn'}
disabledClassName={'paginationDisbaled'}
activeClassName={'paginationActive'}
pageRangeDisplayed={1}
marginPagesDisplayed={2}
/>
</div>
</div>
</>
: <h1>No results found</h1>}
</> :
<>
<h1>Loading...</h1>
</>
}
</div >
} </div>
</div>
</>
)
}
export default Shop;
<!-- end snippet -->
Would love some advice as I've been stuck on this for a couple of days now!
I have attached a simplified codesandbox:
https://github.com/chinapicke/shopit/blob/bb826719ea3a99bf1864ff676821e7dcc2d5ec5d/src/Pages/Shop.js
答案1
得分: 0
不运行您的代码,我认为您的根本问题是 productPerPage
的值 - 正如您已经指出的,它抱怨 pageCount
不是整数,但实际上问题在于使用原始的 event.target.value
来设置 setProductPerPage
,而这是一个字符串 - 您需要使用 parseInt
或类似的方法来获取一个数字,并正确地将其设置为状态。
目前的结果是,您正在使用 slice()
来获取产品的一部分,似乎一旦到达第二页,您提供给 slice
的第二个参数不是有效的数字,因此最终切片如下:slice(pagesVisited)
,意味着只提供一个参数,因此在起始值之后接收所有元素,例如在这里显示。
顺便提一下:要注意正确的命名,我很难理解您的代码,例如,pagesVisited
应该被称为 offset
等等 - 有一些良好的实践方法可以极大地提高代码的可维护性
英文:
Without having run your code I'd say your underlying problem is the productPerPage
value - as you already pointed out, it complains about pageCount
not being an integer, but actually the issue is using setProductPerPage
with the raw event.target.value
, which is a string - you would need to use parseInt
or something to get a number, and set it to the state correctly.
The current result is, you are using slice()
to get a portion of your products, and it seems like, once you get to the 2nd page, the 2nd argument you are providing to slice
is not a valid number, therefore you end up slicing like this: slice(pagesVisited)
, meaning only providing one argument, and therefore receiving all elements after the start value, as shown here for example.
Just a side note: be careful about proper naming, it was difficult for me to understand your code, for instance, pagesVisited
should rather be called offset
etc. - there are some good practises out there that will improve maintainability of your code immensely
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论