【问题标题】:Method not getting correct useState value despite updating state in React尽管在 React 中更新了状态,但方法没有获得正确的 useState 值
【发布时间】:2021-01-09 07:56:19
【问题描述】:

我正在尝试显示分页的学生信息的网站。例如,我有 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;

【问题讨论】:

  • 我无法在 snadbox 中运行您的示例,您可以制作一个代码nadbox 来解释问题所在吗?这是代码:codesandbox.io/s/adoring-hooks-wlcwm
  • 这里是 Adir codesandbox.io/s/hook-issue-7b57d-7b57d,当我单击下一步按钮时,它转到第 2 页、第 3 页,这很好,问题是当它调用 updatePagination 函数时,调用此方法时 pageCount 始终为 0尽管更新了 handleNext 上的 pageCount
  • 你能解释一下这个条件背后的原因吗:pageCount + limit === currentPage inside handleNext function?
  • Yousaf 用户出现以下分页 当用户达到 3 时,它应该将项目更新为 所以基本上当你到达最后一个可见项目时,你会增加计数和用它来切片数组。我有一个分页数组,它本质上是学生总数 1 2 3 4 5 6 7 8 9 10 11 12 使用下一个按钮时我将计数增加了限制,在这种情况下为 3,所以使用见 123,然后是 456 等。 .. 接受更简单的解决方案或想法

标签: javascript reactjs pagination use-state


【解决方案1】:

以下是代码中的问题:

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

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

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

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

    类似地转到上一页:

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

    这将确保正确更新状态。

编辑:

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

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

要实现所需的功能,您需要采取以下步骤:

  1. 要填充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. 要一次显示有限数量的学生,请创建一个函数,根据 currentPagelimitstudents 数组进行切片。

    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);
    }
    

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

演示

下面的代码 sn -p 显示一个例子:

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

【讨论】:

  • Yousaf,非常感谢您的建议和建议。我根据您的反馈和示例进行了一些调整。你可以在这里查看我的最新版本codesandbox.io/s/hook-issue-7b57d-7b57d。我关于如何更新我的页数的问题仍然有效。我在下一个和上一个句柄中硬编码了页数限制,以清楚地传达我想要完成的任务。在您的示例中,分页将显示所有学生/限制,但在某些情况下,我有很多学生同时显示它们会占用大量水平空间。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多