React + Redux 自定义分页实现教程:前端内存分页与服务端分页双方案

胖丽吖_1829

胖丽吖_1829

2026-09-26

123人浏览

原创

React + Redux 自定义分页实现教程:前端内存分页与服务端分页双方案

本文详解如何在 react + redux 应用中实现健壮、可维护的分页逻辑,涵盖服务端分页(推荐)和前端内存分页两种主流模式,并提供完整 slice 结构、selector 计算逻辑及组件集成示例。

本文详解如何在 react + redux 应用中实现健壮、可维护的分页逻辑,涵盖服务端分页(推荐)和前端内存分页两种主流模式,并提供完整 slice 结构、selector 计算逻辑及组件集成示例。

在构建数据密集型 React 应用时,分页不仅是用户体验的关键,更是性能优化的必选项。Redux 本身不内置分页能力,但通过合理设计状态结构与数据流,可轻松支撑两种典型场景:服务端分页(API 支持 limit/offset 或 page/size)与前端内存分页(API 返回全量数据,由前端切片展示)。下面以现代 Redux Toolkit(RTK)为基准,给出生产就绪的实现方案。

✅ 方案一:服务端分页(推荐,适用于大多数真实项目)

当后端 API 支持分页参数(如 /api/items?page=2&size=10),前端只需管理当前页码、每页条数,并触发带参数的请求:

// features/pagination/paginationSlice.ts
import { createSlice, PayloadAction } from '@reduxjs/toolkit';

interface PaginationState {
  currentPage: number;
  itemsPerPage: number;
  totalItems?: number; // 可选:用于计算总页数
  loading: boolean;
  error: string | null;
}

const initialState: PaginationState = {
  currentPage: 1,
  itemsPerPage: 10,
  loading: false,
  error: null,
};

export const paginationSlice = createSlice({
  name: 'pagination',
  initialState,
  reducers: {
    setPage: (state, action: PayloadAction<number>) => {
      state.currentPage = Math.max(1, action.payload);
    },
    setItemsPerPage: (state, action: PayloadAction<number>) => {
      state.itemsPerPage = Math.max(1, action.payload);
      state.currentPage = 1; // 切换每页数量时重置到第 1 页
    },
    setLoading: (state, action: PayloadAction<boolean>) => {
      state.loading = action.payload;
    },
    setError: (state, action: PayloadAction<string null>) => {
      state.error = action.payload;
    },
    setTotalItems: (state, action: PayloadAction<number>) => {
      state.totalItems = action.payload;
    },
  },
});

export const {
  setPage,
  setItemsPerPage,
  setLoading,
  setError,
  setTotalItems,
} = paginationSlice.actions;

export default paginationSlice.reducer;</number></string></boolean></number></number>

配合 Thunk 或 RTK Query,在获取数据时动态拼接参数:

// 假设使用 RTK Query
// api/endpoints/itemsApi.ts
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';

export const itemsApi = createApi({
  baseQuery: fetchBaseQuery({ baseUrl: '/api' }),
  endpoints: (builder) => ({
    getItems: builder.query<item page: number size:>({
      query: ({ page, size }) => `/items?page=${page}&size=${size}`,
      // 可选:自动更新 totalItems
      async onQueryStarted(_, { dispatch, queryFulfilled }) {
        try {
          const { data } = await queryFulfilled;
          // 若响应含 meta.total,可同步更新 pagination 状态
          dispatch(setTotalItems(data.meta?.total || 0));
        } catch {}
      },
    }),
  }),
});</item>

组件中使用:

import { useSelector, useDispatch } from 'react-redux';
import { useGetItemsQuery } from '@/api/endpoints/itemsApi';
import { setPage, setItemsPerPage } from '@/features/pagination/paginationSlice';

function ItemList() {
  const dispatch = useDispatch();
  const { currentPage, itemsPerPage } = useSelector((state: RootState) => state.pagination);

  const { data, isLoading, isError } = useGetItemsQuery({ page: currentPage, size: itemsPerPage });

  const totalPages = useSelector((state: RootState) =>
    state.pagination.totalItems ? Math.ceil(state.pagination.totalItems / itemsPerPage) : 1
  );

  const handlePageChange = (page: number) => {
    if (page >= 1 && page 
      {isLoading && <p>Loading...</p><div class="aritcle_card flexRow artxards">
											<div class="artcardd flexRow">
												<a class="aritcle_card_img" rel="nofollow" href="/xiazai/skill5092" title="PigX UI 前端开发"><img
														src="https://img.php.cn/upload/skill/000/000/081/179033410052138.jpg" alt="PigX UI 前端开发" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a>
												<div class="aritcle_card_info flexColumn">
													<a rel="nofollow" href="/xiazai/skill5092" title="PigX UI 前端开发" class="overflowclass">PigX UI 前端开发</a>
													<p class="overflowclass">PigX UI Pro 前端开发指南 - Vue 3 + TypeScript + Element Plus。当用户提到 PigX UI、PigX 前端、lgb-mgui 项目、Vue 3 企业级后台开发、Element Plus 后台开发时使用此技能。</p>
												</div>
												<a rel="nofollow" href="/xiazai/skill5092" title="PigX UI 前端开发" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span>
												</a>
											</div>
										</div>}
      {isError && <p>Error loading items.</p>}
      {data?.map(item => (
        <div key="{item.id}">{item.title}</div>
      ))}

      {/* 分页控件 */}
      <div classname="pagination">
        <button onclick="{()"> handlePageChange(currentPage - 1)} disabled={currentPage 
          Previous
        </button>
        <span>Page {currentPage} of {totalPages}</span>
        <button onclick="{()"> handlePageChange(currentPage + 1)} disabled={currentPage >= totalPages}>
          Next
        </button>
      </div>
    
  );
}

export default ItemList;

✅ 方案二:前端内存分页(仅限小数据集或原型验证)

当后端无法分页(返回全部 5000 条数据),且你确认内存开销可控时,可采用纯前端切片方式。此时需将 items 存入 Redux(或作为缓存状态),并通过 Selector 实时计算当前页数据:

// features/pagination/memoryPaginationSlice.ts
import { createSlice, PayloadAction } from '@reduxjs/toolkit';

interface MemoryPaginationState {
  items: any[]; // 替换为具体类型
  currentPage: number;
  itemsPerPage: number;
}

const initialState: MemoryPaginationState = {
  items: [],
  currentPage: 1,
  itemsPerPage: 10,
};

export const memoryPaginationSlice = createSlice({
  name: 'memoryPagination',
  initialState,
  reducers: {
    setItems: (state, action: PayloadAction<any>) => {
      state.items = action.payload;
      state.currentPage = 1; // 加载新数据时重置页码
    },
    nextPage: (state) => {
      const maxPage = Math.ceil(state.items.length / state.itemsPerPage);
      if (state.currentPage  {
      if (state.currentPage > 1) state.currentPage -= 1;
    },
    goToPage: (state, action: PayloadAction<number>) => {
      const maxPage = Math.ceil(state.items.length / state.itemsPerPage);
      state.currentPage = Math.max(1, Math.min(action.payload, maxPage));
    },
    setItemsPerPage: (state, action: PayloadAction<number>) => {
      state.itemsPerPage = Math.max(1, action.payload);
      state.currentPage = 1;
    },
  },
});

// Selector:安全切片,避免越界
export const selectCurrentPageItems = (state: RootState) => {
  const { items, currentPage, itemsPerPage } = state.memoryPagination;
  const startIndex = (currentPage - 1) * itemsPerPage;
  return items.slice(startIndex, startIndex + itemsPerPage);
};</number></number></any>

组件中消费:

import { useSelector } from 'react-redux';
import { selectCurrentPageItems } from '@/features/pagination/memoryPaginationSlice';

function MemoryPaginatedList() {
  const items = useSelector(selectCurrentPageItems);
  const { currentPage, itemsPerPage } = useSelector((state: RootState) => state.memoryPagination);

  const totalPages = Math.ceil(useSelector((s: RootState) => s.memoryPagination.items.length) / itemsPerPage);

  return (
    <div>
      {items.map(item => (
        <div key="{item.id}">{item.name}</div>
      ))}
      <div classname="pagination">
        <button onclick="{()"> dispatch(prevPage())} disabled={currentPage ← Prev</button>
        <span>{currentPage} / {totalPages}</span>
        <button onclick="{()"> dispatch(nextPage())} disabled={currentPage >= totalPages}>Next →</button>
      </div>
    </div>
  );
}

⚠️ 注意事项与最佳实践

  • 永远优先选择服务端分页:避免前端加载冗余数据,降低首屏时间与内存压力;
  • 防抖页码变更:在用户快速点击“下一页”时,可结合 throttle 或取消未完成请求;
  • 保持状态一致性:切换 itemsPerPage 时应重置 currentPage = 1,否则可能出现空页;
  • 总页数计算容错:Math.ceil(total / perPage) 在 total = 0 时需兜底为 1;
  • TypeScript 类型强化:为 items 使用泛型接口(如 Item[]),提升可维护性;
  • 不要在 reducer 中执行副作用(如 API 调用),交由 Thunk 或 RTK Query 处理。

通过以上两种模式的清晰划分与代码封装,你不仅能灵活应对不同后端能力,还能深入理解 Redux 状态驱动 UI 的核心思想——分页的本质,是「状态映射到视图」的精准表达。

前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!

相关文章

PHP速学视频免费教程(入门到精通)
PHP速学视频免费教程(入门到精通)

PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

react 前端

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
python是前端还是后端
python是前端还是后端

Python属于前端也属于后端,其灵活性和丰富的生态系统使得开发人员能够在不同的领域中灵活运用。本专题为大家提供python相关的文章、下载、课程内容,供大家免费下载体验。

2023.08.11

2123

5

前端如何实现即时通讯
前端如何实现即时通讯

实现即时通讯的方法有WebSocket、Long Polling、Server-Sent Events、WebRTC等等。详细介绍:1、WebSocket,它可以在客户端和服务器之间建立持久连接,实现实时的双向通信,前端可以使用 WebSocket API来创建WebSocket连接,并通过发送和接收消息来实现即时通讯;2、Long Polling,是一种模拟实时通信的技术等等。

2023.10.09

4543

6

前端和后端的区别
前端和后端的区别

前端关注的是用户界面的设计和交互,而后端则注重数据处理和逻辑控制。想了解更多前端后端的相关内容,可以阅读本专题下面的文章。

2024.03.19

5690

13

php和前端的关联介绍
php和前端的关联介绍

php既可以作为前端语言,也可以作为后端语言。想了解更多php和前端的相关内容,可以阅读本专题下面的文章。

2024.03.22

5118

10

前端外包工作内容有哪些
前端外包工作内容有哪些

前端外包工作内容包括:1. 网站和应用程序开发;2. 用户界面和交互设计;3. 用户体验优化;4. 设计和视觉开发;5. 跨浏览器兼容性;6. 性能优化;7. 维护和更新;8. 项目管理和沟通。想了解更多前端的相关内容,可以阅读本专题下面的文章。

2024.05.22

723

5

js获取数组长度的方法
js获取数组长度的方法

在js中,可以利用array对象的length属性来获取数组长度,该属性可设置或返回数组中元素的数目,只需要使用“array.length”语句即可返回表示数组对象的元素个数的数值,也就是长度值。php中文网还提供JavaScript数组的相关下载、相关课程等内容,供大家免费下载使用。

2023.06.20

4226

5

js刷新当前页面
js刷新当前页面

js刷新当前页面的方法:1、reload方法,该方法强迫浏览器刷新当前页面,语法为“location.reload([bForceGet]) ”;2、replace方法,该方法通过指定URL替换当前缓存在历史里(客户端)的项目,因此当使用replace方法之后,不能通过“前进”和“后退”来访问已经被替换的URL,语法为“location.replace(URL) ”。php中文网为大家带来了js刷新当前页面的相关知识、以及相关文章等内容

2023.07.04

1069

3

js四舍五入
js四舍五入

js四舍五入的方法:1、tofixed方法,可把 Number 四舍五入为指定小数位数的数字;2、round() 方法,可把一个数字舍入为最接近的整数。php中文网为大家带来了js四舍五入的相关知识、以及相关文章等内容

2023.07.04

4124

6

js删除节点的方法
js删除节点的方法

js删除节点的方法有:1、removeChild()方法,用于从父节点中移除指定的子节点,它需要两个参数,第一个参数是要删除的子节点,第二个参数是父节点;2、parentNode.removeChild()方法,可以直接通过父节点调用来删除子节点;3、remove()方法,可以直接删除节点,而无需指定父节点;4、innerHTML属性,用于删除节点的内容。

2023.09.01

840

4

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
React 教程
React 教程

共58课时 | 11.9万人学习

国外Web开发全栈课程全集
国外Web开发全栈课程全集

共12课时 | 1.4万人学习