简体   繁体   English

尽管在 React 中更新了 state,方法仍未获得正确的 useState 值

[英]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将为truepageCount将设置为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 已正确更新。

Edit:编辑:

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:要实现所需的功能,您需要执行以下步骤:

  1. 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]);
  2. To display limited number of students at a time, create a function that slices the students array depending on the currentPage and limit .要一次显示有限数量的学生,请创建一个 function,它根据currentPagelimitstudents数组进行切片。

     const getPaginatedStudents = () => { const startIndex = currentPage * limit - limit; const endIndex = startIndex + limit; return students.slice(startIndex, endIndex); };
  3. 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 来根据currentPagelimitpagination数组进行切片。 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); };
  4. 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.这就是获得所需功能所需的全部。

Demo演示

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 上查看此演示。

编辑 crazy-wescoff-2jttn

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM