[英]Method not getting correct useState value despite updating state in React
I am working on a site where I am trying to display paginated student information.我在一个试图显示分页学生信息的网站上工作。 For example I have 12 students I have pagination items 1-4 as I display 3 students at a time.
例如,我有 12 个学生,我有分页项目 1-4,因为我一次显示 3 个学生。
I am running into a problem when I call my update pagination function. The counter in this method is always 0 despite me incrementing it correctly.当我调用我的更新分页 function 时,我遇到了一个问题。尽管我正确地递增了它,但此方法中的计数器始终为 0。 Any ideas or pointers would be greatly appreciated.
任何想法或指示将不胜感激。
import React, {useState, useEffect} from 'react';
function App() {
let students = [
{'name': 'Harry'},
{'name': 'Hermoine'},
{'name': 'Ron'},
{'name': 'Ginny'},
{'name': 'Snape'},
{'name': 'Bellatrix'},
{'name': 'Albus'},
{'name': 'Dudley'},
{'name': 'Petunia'},
{'name': 'Hagrid'},
{'name': 'Lee'},
{'name': 'James'}
];
let [loaded, setLoaded] = useState(false);
let [pagination, setPagination] = useState([]);
let [limit, setLimit] = useState(3); // how many students are visible at a time
let [pages, setPages] = useState(Math.round(students.length/limit)); // amount of pages based on total students
let [currentPage, setCurrentPage] = useState(1); // the current page
let [pageCount, setPageCount] = useState(0); // counter used to increment/decrement pages in view
function updatePagination() {
let tmpArray = [];
for(let i = pageCount; i < pages; i ++){
tmpArray.push(i+1);
}
// 1-3, 4-6 etc...
setPagination(tmpArray.slice(pageCount, pageCount + limit)); // this pageCount is always 0 despite me setting it on handleNext method
}
function handleNext(){
setCurrentPage(currentPage + 1);
if(pageCount + limit === currentPage){
if(currentPage <= pages){
setPageCount(pageCount + limit);
}
}
updatePagination();
}
useEffect(() => {
updatePagination();
setLoaded(true);
}, []);
return (
<main className={styles.app}>
<div className={styles.student}>
<h1>Three student cards</h1>
</div>
<ol className={styles.pagination}>
<div className={styles.paginationContainer}>
<button className={currentPage === 0 ? styles.prev + ' ' + styles.disabled : styles.prev}>prev</button>
{loaded && pagination.map((item, index) => {
return (
<li key={index} className={styles.paginationItem}>
<button className={currentPage === item ? styles.active : null}>{item}</button>
</li>
)
})}
<button onClick={() => {
handleNext()
}} className={currentPage === pages ? styles.next + ' ' + styles.disabled : styles.next}>next</button>
</div>
</ol>
{loaded &&
<div className={styles.info}>
<p>Page Count: {pageCount}</p>
<p>Current Page: {currentPage}</p>
<p>Limit: {limit}</p>
<p>Pages: {pages}</p>
<p>Total Students: {students.length}</p>
<p>Pagination: {pagination}</p>
</div>
}
</main>
);
}
export default App;
Following are the problems in your code:以下是您的代码中的问题:
Inside handleNext()
function, you are using currentPage
immediately after calling setCurrentPage(...)
function BUT the state is updated asynchronously.在
handleNext()
function 内部,您在调用setCurrentPage(...)
function 后立即使用currentPage
,但 state 是异步更新的。 So currentPage
will be the old value instead of the updated value.所以
currentPage
将是旧值而不是更新值。
Another problem is that if the user clicks the next
button three times, then the conditon pageCount + limit === currentPage
will be true
and pageCount
will be set to pageCount + limit
which is 3. This means that the loop inside handleNext()
function for (let i = pageCount; i < pages; i++) {...}
will only execute once.另一个问题是,如果用户点击
next
按钮三次,那么条件pageCount + limit === currentPage
将为true
, pageCount
将设置为pageCount + limit
,即 3。这意味着handleNext()
function 中的循环for (let i = pageCount; i < pages; i++) {...}
只会执行一次。 So tmpArray
will contain only one element, ie 4.所以
tmpArray
将只包含一个元素,即 4。
Also, since calling handleNext()
function updates the currentPage
depending on its previous value, you should update the state by passing the function to setCurrentPage()
function此外,由于调用
handleNext()
function 会根据其先前的值更新currentPage
,因此您应该通过将 function 传递给setCurrentPage()
function 来更新 state
setCurrentPage((page) => page + 1);
Similarly to go to previous page:类似于上一页的 go:
setCurrentPage((page) => page - 1);
This will make sure that state is updated correctly.这将确保 state 已正确更新。
It wasn't clear before why you were using pageCount
but the demo you posted in a comment made it clear what you are trying to achieve.之前并不清楚您为什么使用
pageCount
,但您在评论中发布的演示清楚地表明了您要实现的目标。
Problem you mentioned in your question is that value of pageCount
is always zero.您在问题中提到的问题是
pageCount
的值始终为零。 Looking at your code, i think you don't need pageCount
at all.查看您的代码,我认为您根本不需要
pageCount
。
To achieve the desired functionality, you need to take following steps:要实现所需的功能,您需要执行以下步骤:
To populate the pagination
array, you can make use of the useEffect
hook that executes whenever students
array or limit
changes.要填充
pagination
数组,您可以使用useEffect
钩子,它会在students
数组或limit
发生变化时执行。
useEffect(() => { // set pagination let arr = new Array(Math.ceil(students.length / limit)).fill().map((_, idx) => idx + 1); setPagination(arr); setLoaded(true); }, [students, limit]);
To display limited number of students at a time, create a function that slices the students
array depending on the currentPage
and limit
.要一次显示有限数量的学生,请创建一个 function,它根据
currentPage
和limit
对students
数组进行切片。
const getPaginatedStudents = () => { const startIndex = currentPage * limit - limit; const endIndex = startIndex + limit; return students.slice(startIndex, endIndex); };
To display a limited number of pages equal to the limit
, you don't need pageCount
.要显示等于
limit
的有限页数,您不需要pageCount
。 You can create a function that slices the pagination
array depending on the currentPage
and limit
.您可以创建一个 function 来根据
currentPage
和limit
对pagination
数组进行切片。 This function is similar to the one created in step 2 but the difference lies in how startIndex
is calculated.这个 function 与步骤 2 中创建的类似,但区别在于
startIndex
的计算方式。
const getPaginationGroup = () => { let start = Math.floor((currentPage - 1) / limit) * limit; let end = start + limit; return pagination.slice(start, end); };
Create functions that will change the currentPage
创建将更改
currentPage
的函数
function goToNextPage() { setCurrentPage((page) => page + 1); } function goToPreviousPage() { setCurrentPage((page) => page - 1); } function changePage(event) { const pageNumber = Number(event.target.textContent); setCurrentPage(pageNumber); }
That's all you need to get the desired functionality.这就是获得所需功能所需的全部。
Following code snippet shows an example:以下代码片段显示了一个示例:
const studentsData = [ { name: "Harry" }, { name: "Hermoine" }, { name: "Ron" }, { name: "Ginny" }, { name: "Snape" }, { name: "Bellatrix" }, { name: "Albus" }, { name: "Dudley" }, { name: "Petunia" }, { name: "Hagrid" }, { name: "Lee" }, { name: "James" }, { name: "Lily" }, { name: "Remus" }, { name: "Voldemort" }, { name: "Dobby" }, { name: "Lucius" }, { name: "Sirius" } ]; function Student({ name }) { return ( <div className="student"> <h3>{name}</h3> </div> ); } function App() { let [students] = React.useState(studentsData); let [pagination, setPagination] = React.useState([]); let [loaded, setLoaded] = React.useState(false); let [limit] = React.useState(3); let [pages] = React.useState(Math.round(students.length / limit)); let [currentPage, setCurrentPage] = React.useState(1); function goToNextPage() { setCurrentPage((page) => page + 1); } function goToPreviousPage() { setCurrentPage((page) => page - 1); } function changePage(event) { const pageNumber = Number(event.target.textContent); setCurrentPage(pageNumber); } React.useEffect(() => { // set pagination let arr = new Array(Math.ceil(students.length / limit)).fill().map((_, idx) => idx + 1); setPagination(arr); setLoaded(true); }, [students, limit]); const getPaginatedStudents = () => { const startIndex = currentPage * limit - limit; const endIndex = startIndex + limit; return students.slice(startIndex, endIndex); }; const getPaginationGroup = () => { let start = Math.floor((currentPage - 1) / limit) * limit; let end = start + limit; return pagination.slice(start, end); }; return ( <main> <h1>Students</h1> {loaded && ( <div className="studentsContainer"> {getPaginatedStudents().map((s) => ( <Student key={s.name} {...s} /> ))} </div> )} <ol className="pagination"> <div className="paginationContainer"> <button onClick={goToPreviousPage} className={currentPage === 1? "prev disabled": "prev"} > prev </button> {loaded && getPaginationGroup().map((item, index) => { return ( <li key={index} className="paginationItem"> <button onClick={changePage} className={currentPage === item? "active": null} > {item} </button> </li> ); })} <button onClick={goToNextPage} className={currentPage === pages? "next disabled": "next"} > next </button> </div> </ol> </main> ); } ReactDOM.render(<App/>, document.getElementById('root'));
h1 { margin: 0; }.studentsContainer { display: flex; background: #efefef; padding: 15px 10px; margin: 5px 0; }.student { flex-grow: 1; box-shadow: 0 0 2px rgba(0, 0, 0, 0.4); text-align: center; max-width: 450px; margin: 0 5px; }.pagination { position: relative; padding: 0; }.paginationContainer { align-items: center; display: grid; grid-template-columns: repeat(auto-fit, minmax(40px, auto)); grid-column-gap: .65rem; justify-content: center; justify-items: center; max-width: 100%; width: 100%; position: relative; }.paginationItem { height: 40px; width: 40px; user-select: none; list-style: none; }.prev, .next { user-select: none; cursor: pointer; background: transparent; border: none; outline: none; }.prev.disabled, .next.disabled { pointer-events: none; opacity: .5; } button { padding: .65rem; display: block; height: 100%; width: 100%; border-radius: 50%; text-align: center; border: 1px solid #ccc; outline: none; cursor: pointer; } button.active { background: rgba(black, .25); border: none; color: #777; pointer-events: none; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script> <div id="root"></div>
You can also view this demo on codesandbox.您也可以在 codesandbox 上查看此演示。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.