0803分页_加载更多-网络ajax请求2-react-仿低代码平台项目
文章目录
- 1 分页
- 1.1 url与分页参数
- 1.2 分页组件与url
- 1.3 列表页引用分页组件
- 2 加载更多
- 2.1 状态
- 2.2 触发时机
- 2.3 加载数据
- 2.4优化
- 结语
1 分页
1.1 url与分页参数
查询问卷列表接口,添加分页参数:
- page:当前页码(第几页)
- pageSize:每页多少条记录
question.ts 查询文件接口列表参数扩展,如下所示:
type SearchOption = {keyword: string;isStar: boolean;isDeleted: boolean;page: number;pageSize: number;
};
浏览器url获取分页参数,useLoadQuestionListData.ts代码如下所示:
import { useSearchParams } from "react-router-dom";
import { useRequest } from "ahooks";
import { getQuestionListApi } from "@/api/question";import {LIST_SEARCH_PARAM_KEY,LIST_PAGE_PARAM_KEY,LIST_PAGE_SIZE_PARAM_KEY,LIST_PAGE_SIZE_DEFAULT,LIST_PAGE_DEFAULT,
} from "@/constant";type OptionType = {isStar: boolean;isDeleted: boolean;page: number;pageSize: number;
};/*** 获取问卷列表* @returns 问卷列表*/
function useLoadQuestionListData(opt: Partial<OptionType>) {const { isStar, isDeleted } = opt;const [searchParams] = useSearchParams();const keyword = searchParams.get(LIST_SEARCH_PARAM_KEY) || "";const page =parseInt(searchParams.get(LIST_PAGE_PARAM_KEY) || "") || LIST_PAGE_DEFAULT ;const pageSize =parseInt(searchParams.get(LIST_PAGE_SIZE_PARAM_KEY) || "") ||LIST_PAGE_SIZE_DEFAULT;async function load() {const data = await getQuestionListApi({keyword,isStar,isDeleted,page,pageSize,});//...
}export default useLoadQuestionListData;
服务端解析分页参数,question.js代码如下:
const Mock = require('mockjs')const getQuestionList = require("./data/getQuestionList")const Random = Mock.Randommodule.exports = [// ...{// 获取问卷列表url: '/api/question',method: 'get',response(ctx) {const { query = {} } = ctxconst isStar = query.isStar === 'true'const isDeleted = query.isDeleted === 'true'const page = parseInt(query.page) || 1const pageSize = parseInt(query.pageSize) || 10return {errno: 0,data: {list: getQuestionList({isStar, isDeleted, page, pageSize}),total: 100}}}},
]
1.2 分页组件与url
antd分页组件与url交互
由于多个列表页面都需要分页组件,这里我们以antd分页组件为基础,封装自定义分页组件,并且实现分页参数与url参数交互,ListPage.tsx代码如下所示:
import { FC, useEffect, useState } from "react";
import { useLocation, useNavigate, useSearchParams } from "react-router-dom";
import { Pagination } from "antd";import {LIST_PAGE_DEFAULT,LIST_PAGE_PARAM_KEY,LIST_PAGE_SIZE_DEFAULT,LIST_PAGE_SIZE_PARAM_KEY,
} from "@/constant";type PropsType = {total: number;
};const ListPage: FC<PropsType> = (props: PropsType) => {const [current, setCurrent] = useState(LIST_PAGE_DEFAULT);const [pageSize, setPageSize] = useState(LIST_PAGE_SIZE_DEFAULT);const [searchParams] = useSearchParams();useEffect(() => {const curPage =parseInt(searchParams.get(LIST_PAGE_PARAM_KEY) || "") ||LIST_PAGE_DEFAULT;const curPageSize =parseInt(searchParams.get(LIST_PAGE_SIZE_PARAM_KEY) || "") ||LIST_PAGE_SIZE_DEFAULT;setCurrent(curPage);setPageSize(curPageSize);}, [searchParams]);const { total } = props;// 当page pageSize改变时,跳转页面(改变url)const nav = useNavigate();const { pathname } = useLocation();function handlePageChange(page: number, pageSize: number) {searchParams.set(LIST_PAGE_PARAM_KEY, page.toString());searchParams.set(LIST_PAGE_SIZE_PARAM_KEY, pageSize.toString());nav({pathname,search: searchParams.toString(),});}return (<Paginationcurrent={current}pageSize={pageSize}total={total}onChange={handlePageChange}/>);
};export default ListPage;
1.3 列表页引用分页组件
“星标问卷”列表页Star.tsx代码如下:
// ...
import ListPage from "@/components/ListPage";const List: FC = () => {// ...//问卷列表数据const { data = {}, loading } = useLoadQuestionListData({ isStar: true });const { list = [], total = 0 } = data;
// ...<div className={styles.footer}><ListPage total={total} /></div></>);
};export default List;
“回收站”列表页Trash.tsx代码如下所示:
import { FC, useState } from "react";
import { useTitle } from "ahooks";
import ListPage from "../../components/ListPage";const List: FC = () => {useTitle("调查问卷-回收站");//问卷列表数据const { data = {}, loading } = useLoadQuestionListData({ isDeleted: true });const { list = [], total = 0 } = data;
// ...<div className={styles.footer}><ListPage total={total} /></div></>);
};export default List;
2 加载更多
2.1 状态
基础
- page:当前页
- list:全部数据列表,上划累加
- total:总条数
计算项
- hasMoreData:是否有更多数据
2.2 触发时机
- 页面加载触发
- 页面滚动到加载更多数据时触发
- 监听页面滚动
- 防抖处理
- DOM计算页面滚动刀”加载更多“
2.3 加载数据
- useRequest请求接口
- 接口返回数据,设置状态
2.4优化
- 加载更多,缓存处理
- 刷新页面优化暂无数据
- 搜索重置状态
完整”我的问卷“页List.tsx代码如下所示:
import { FC, useEffect, useMemo, useRef, useState } from "react";
import { useSearchParams } from "react-router-dom";
import { useDebounceFn, useRequest, useTitle } from "ahooks";
import { Typography, Spin, Empty } from "antd";import QuestionCard from "@/components/QuestionCard";
import ListSearch from "@/components/ListSearch";
import styles from "./common.module.scss";
import { getQuestionListApi } from "@/api/question";
import { LIST_PAGE_SIZE_DEFAULT, LIST_SEARCH_PARAM_KEY } from "@/constant";const { Title } = Typography;const List: FC = () => {useTitle("调查问卷-我的问卷");//问卷列表数据const [started, setStarted] = useState(false);const [page, setPage] = useState(1);const [list, setList] = useState([]); // 全部列表数据,上划加载更多,累计const [total, setTotal] = useState(0);const hasMoreData = total > list.length;const [searchParams] = useSearchParams();const keyword = searchParams.get(LIST_SEARCH_PARAM_KEY) || "";// 搜索重置状态useEffect(() => {setStarted(false);setPage(1);setList([]);setTotal(0);}, [keyword]);// 加载数据const { run: loadData, loading } = useRequest(async () => {const data = await getQuestionListApi({page,pageSize: LIST_PAGE_SIZE_DEFAULT,keyword,});return data;},{manual: true,onSuccess(res) {const { list: newList = [], total = 0 } = res;// 累计数据setList(list.concat(newList));setTotal(total);setPage(page + 1);},});// 尝试触发加载-防抖处理const containerRef = useRef<HTMLDivElement>(null);const { run: tryLoadMore } = useDebounceFn(() => {const elem = containerRef.current;if (elem == null) {return;}// 判断如果div bottom 小于等于页面的高度const domRect = elem.getBoundingClientRect();if (domRect == null) {return;}const { bottom } = domRect;if (bottom <= document.body.clientHeight) {// 加载数据loadData();setStarted(true);}},{ wait: 500 });// 触发时机:页面加载或者url参数(keyword)变化时useEffect(() => {// 第一次加载,初始化tryLoadMore();}, [searchParams]);// 监听页面滚动事件useEffect(() => {if (hasMoreData) {window.addEventListener("scroll", tryLoadMore);}return () => {// 解绑事件window.removeEventListener("scroll", tryLoadMore);};}, [searchParams, hasMoreData]);// 加载更多显示优化const LoadMoreContentElem = useMemo(() => {if (!started || loading) {return <Spin />;}if (total === 0) {return <Empty description="暂无数据" />;}if (!hasMoreData) {return <span>没有更多了……</span>;}return <span>开始加载下一页</span>;}, [started, loading, hasMoreData]);return (<><div className={styles.header}><div className={styles.left}><Title level={3}>我的问卷</Title></div><div className={styles.right}><ListSearch /></div></div><div className={styles.content}>{list.length > 0 &&list.map((q: any) => {const { _id } = q;return <QuestionCard key={_id} {...q} />;})}</div><div className={styles.footer}><div ref={containerRef}>{LoadMoreContentElem}</div></div></>);
};export default List;
结语
❓QQ:806797785
⭐️仓库地址:https://gitee.com/gaogzhen
⭐️仓库地址:https://github.com/gaogzhen
[1]ahook官网[CP/OL].
[2]mock文档[CP/OL].
[3]Ant Design官网[CP/OL].