尽管在 React 中更新了状态,但方法没有获得正确的 useState 值

Posted

技术标签:

【中文标题】尽管在 React 中更新了状态,但方法没有获得正确的 useState 值【英文标题】:Method not getting correct useState value despite updating state in React 【发布时间】: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 等。 .. 接受更简单的解决方案或想法 【参考方案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

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

    要填充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]);
    

    要一次显示有限数量的学生,请创建一个函数,根据 currentPagelimitstudents 数组进行切片。

    const getPaginatedStudents = () => 
       const startIndex = currentPage * limit - limit;
       const endIndex = startIndex + limit;
    
       return students.slice(startIndex, endIndex);
    ;
    

    要显示等于limit 的有限页数,您不需要pageCount。您可以创建一个函数,根据currentPagelimitpagination 数组进行切片。此函数与第 2 步中创建的函数类似,但不同之处在于 startIndex 的计算方式。

    const getPaginationGroup = () => 
       let start = Math.floor((currentPage - 1) / limit) * limit;
       let end = start + limit;
    
       return pagination.slice(start, end);
    ;
    

    创建将更改 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。我关于如何更新我的页数的问题仍然有效。我在下一个和上一个句柄中硬编码了页数限制,以清楚地传达我想要完成的任务。在您的示例中,分页将显示所有学生/限制,但在某些情况下,我有很多学生同时显示它们会占用大量水平空间。

以上是关于尽管在 React 中更新了状态,但方法没有获得正确的 useState 值的主要内容,如果未能解决你的问题,请参考以下文章

在 C/C++ 中获得正模的最快方法

React setState 没有得到更新

为啥尽管观察者的状态发生变化,但应用程序重启后 Flutter BloC UI 没有更新?

如何获得正斜杠网址? [重复]

在 React 上下文中更新嵌套状态的最佳方法

react setState 回调没有更新状态