You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React分页组件setState后DOM未重渲染问题求助

React分页组件问题排查与修复

问题分析

你遇到的三个核心问题原因如下:

  1. 切换页码需点击两次才更新:React的setState是异步操作,在paginationController中直接使用currentPage、lastIndex等状态值计算新索引时,拿到的是更新前的旧值,导致第一次点击时切片范围错误,第二次点击才会使用正确的状态值。
  2. 分页前后按钮无法使用:代码中给Pagination组件添加了hideNextButton和hidePrevButton属性,直接隐藏了前后导航按钮,自然无法触发点击。
  3. DOM未触发重渲染:slicedUsers的更新依赖异步状态的变化,同步计算无法保证状态更新的顺序,导致组件没有感知到数据变化。

修复方案与代码

我们可以通过简化状态管理和修正逻辑依赖来解决这些问题:

  • 移除firstIndex、lastIndex、slicedUsers这些冗余状态,直接通过currentPage和countPerPage计算派生值,避免状态同步问题。
  • 移除Pagination的隐藏按钮属性,恢复前后导航功能。
  • 使用Math.ceil计算总页数,确保最后一页的剩余数据能正常展示。
  • 若需要缓存切片结果,可通过useMemo优化,或者直接在渲染时计算。

修复后的完整代码:

import { useState, useEffect } from 'react';
import { Container, Paper, Table, TableHead, TableCell, TableBody, TableRow, CircularProgress, Pagination } from '@mui/material';

function App() {
  const [users, setUsers] = useState([]);
  const [currentPage, setCurrentPage] = useState(1);
  const [isLoading, setIsLoading] = useState(true);
  
  const countPerPage = 5;
  // 计算总页数,用ceil确保包含剩余数据
  const allPageCount = Math.ceil(users.length / countPerPage);
  // 直接计算当前页的切片数据,无需维护额外状态
  const slicedUsers = users.slice(
    (currentPage - 1) * countPerPage,
    currentPage * countPerPage
  );

  useEffect(() => {
    fetch("https://jsonplaceholder.typicode.com/posts")
      .then((res) => res.json())
      .then((data) => {
        setUsers(data);
        setIsLoading(false);
      });
  }, []);

  const paginationController = (e, value) => {
    // 直接更新当前页码,切片数据会自动重新计算
    setCurrentPage(value);
  };

  return (
    <>
      <Container
        sx={{
          display: "flex",
          flexDirection: "column",
          gap: "10px",
          height: "100svh",
          justifyContent: "center",
          alignItems: "center",
        }}
      >
        {isLoading ? (
          <CircularProgress color="warning" />
        ) : (
          <>
            <TableContainer
              component={Paper}
              sx={{ boxShadow: "0 0 12px #15544b" }}
            >
              <Table>
                <TableHead>
                  <TableRow>
                    <TableCell>ID</TableCell>
                    <TableCell>Title</TableCell>
                    <TableCell>Body</TableCell>
                  </TableRow>
                </TableHead>

                <TableBody>
                  {slicedUsers.map((user) => (
                    <TableRow key={user.id}>
                      <TableCell>{user.id}</TableCell>
                      <TableCell>{user.title}</TableCell>
                      <TableCell>{user.body}</TableCell>
                    </TableRow>
                  ))}
                </TableBody>
              </Table>
            </TableContainer>

            <Pagination
              count={allPageCount}
              color="primary"
              onChange={paginationController}
              page={currentPage}
            />
          </>
        )}
      </Container>
    </>
  );
}

export default App;

关键修改点说明

  • 移除了firstIndex、lastIndex、slicedUsers状态,改为直接通过currentPage计算切片范围,避免异步状态更新导致的不一致。
  • 移除hideNextButton和hidePrevButton,恢复分页组件的前后导航按钮。
  • 给Pagination添加page={currentPage}属性,确保组件显示当前选中的页码。
  • 用Math.ceil计算总页数,保证所有数据都能被展示。
  • 修复了原代码中TableHead内缺少TableRow的问题,符合Material UI的组件结构要求。

内容的提问来源于stack exchange,提问作者Farid Lotfi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 04:43:32