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

Method not getting correct useState value despite updating state in React

我在一个网站上工作,试图显示分页的学生信息。例如,我有 12 个学生我有分页项目 1-4,因为我一次显示 3 个学生。

我在调用更新分页函数时遇到了问题 运行。尽管我正确地增加了它,但此方法中的计数器始终为 0。任何想法或指示将不胜感激。

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;

以下是您的代码中的问题:

  • handleNext() 函数中,您在调用 setCurrentPage(...) 函数后立即使用 currentPage 但状态是异步更新的。所以 currentPage 将是旧值而不是更新值。

  • 另一个问题是,如果用户点击next按钮三次,那么条件pageCount + limit === currentPage将是true,而pageCount将设置为 pageCount + limit 即 3。这意味着 handleNext() 函数 for (let i = pageCount; i < pages; i++) {...} 内的循环将只执行一次。所以 tmpArray 将只包含一个元素,即 4.

  • 此外,由于调用 handleNext() 函数会根据其先前的值更新 currentPage,您应该通过将函数传递给 setCurrentPage() 函数来更新状态

    setCurrentPage((page) => page + 1);
    

    同样转到上一页:

    setCurrentPage((page) => page - 1);
    

    这将确保状态更新正确。

编辑:

之前不清楚您为什么使用 pageCount,但您在评论中发布的演示清楚地说明了您要实现的目标。

您在问题中提到的问题是 pageCount 的值始终为零。查看您的代码,我认为您根本不需要 pageCount

要实现所需的功能,您需要执行以下步骤:

  1. 要填充 pagination 数组,您可以使用每当 students 数组或 limit 更改时执行的 useEffect 挂钩。

    useEffect(() => {
       // set pagination
       let arr = new Array(Math.ceil(students.length / limit))
          .fill()
          .map((_, idx) => idx + 1);
    
       setPagination(arr);
       setLoaded(true);
    
    }, [students, limit]);
    
  2. 要一次显示有限数量的学生,请创建一个根据 currentPagelimit.[=52 对 students 数组进行切片的函数=]

    const getPaginatedStudents = () => {
       const startIndex = currentPage * limit - limit;
       const endIndex = startIndex + limit;
    
       return students.slice(startIndex, endIndex);
    };
    
  3. 要显示等于 limit 的有限页数,您不需要 pageCount。您可以创建一个函数,根据 currentPagelimitpagination 数组进行切片。此函数与步骤 2 中创建的函数类似,不同之处在于 startIndex 的计算方式。

    const getPaginationGroup = () => {
       let start = Math.floor((currentPage - 1) / limit) * limit;
       let end = start + limit;
    
       return pagination.slice(start, end);
    };
    
  4. 创建将改变 currentPage

    的函数
    function goToNextPage() {
       setCurrentPage((page) => page + 1);
    }
    
    function goToPreviousPage() {
       setCurrentPage((page) => page - 1);
    }
    
    function changePage(event) {
       const pageNumber = Number(event.target.textContent);
       setCurrentPage(pageNumber);
    }
    

这就是您获得所需功能所需的全部内容。

演示

以下代码片段显示了一个示例:

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>

您也可以在 codesandbox 上查看此演示。