列表页面(加载更多)

仅当使用 antd-mobile@2 的时候,需要用到它。antd-mobile@5 的时候,请使用官方的 InfiniteScrollList 来实现这个功能。

仅当使用 antd-mobile@2 的时候,需要用到它。antd-mobile@5 的时候,请使用官方的 InfiniteScrollList 来实现这个功能。

(上面的重复是有意为之,无需移除)

移动端的加载更多页面封装,简化业务流程。使用了 @umijs/hooksuseLoadMore

最简 Demo

import LoadMoreListView from '@alitajs/list-view';
import { request } from 'alita';
 
export async function query(data): Promise<any> {
  return request('/api/list', { data });
}
 
const IndexPage: FC = () => {
  const renderRow = rowData => (
    <div style={{ height: 500 }}>{rowData.title}</div>
  );
  return (
    <LoadMoreListView
      requestFunc={query}
      renderRow={renderRow}
      requestParams={{
        abc: '123',
        token: 'alita',
        pageSize: 0,
        offset: 0,
      }}
    />
  );
};
 
export default IndexPage;

API

参数说明类型默认值是否必填
height滚动容器的高度string充满剩余容器高度
alias请求参数的别名AliasProps见表格下方备注
requestFunc请求执行函数异步函数
noData空列表展示内容string or node''
requestParams请求参数object
renderRow从数据源(data source)中接受一条数据,以及它和它所在 section 的 ID。返回一个可渲染的组件来为这行数据进行渲染。默认情况下参数中的数据就是放进数据源中的数据本身,不过也可以提供一些转换器。如果某一行正在被高亮(通过调用 highlightRow 函数),ListView 会得到相应的通知。(rowData, sectionID, rowID, highlightRow) => renderable
renderFooter重新渲染页脚,会传入三个参数,表示列表页面的当前状态。( noMore, loadingMore, loadMore?) => React.ReactElement''
isTabsPage在 Tabs 页面中使用时需要配置,高度需要减去底部 tabs 高度false
onChange数据变化时回调,你可以通过它取得当前列表的所有值(data)=>void
autoFullViewPort首屏数据不能覆盖全屏时,自动加载更多填充屏幕,解决因首次加载数据条目太少导致无法触发加载更多的 bug,版本0.3.7以后支持。boolean
startPage起始加载页,如果startPage设置为 0, 刷新时会将pageNum重置为 0, 版本1.0.1-beta以后支持。number1
其他 ListView 参数能接收 ListView 的其他参数,请注意不要设置 'onEndReached'、 'dataSource'

如果不需要下拉刷新的功能,可以设置 pullToRefresh=

AliasProps

interface AliasProps {
  data?: string;
  pageSize?: string;
  offset?: string;
  total?: string;
}

alias 和 requestParams

默认约定请求参数是 { pageSize, offset } ,返回的数据是 { data, total }。如果你的请求参数和返回数据不是按照约定,那你需要手动设置 alias。如你的返回数据是 { list, count },那你需要设置 alias{ data: 'list', total: 'count' }。如果你的请求参数,除了 pageSizeoffset 之外,还有其它的参数,那你需要设置 requestParamsrequestParams 中的 pageSizeoffset 会被组件接管和覆盖,在加载更多时,自动产生变化,你无需理会。

requestParams 发生改变的时候,会自动执行 reload。无需重复编写逻辑。

约定出入参

interface Params {
  pageSize: number;
  offset: number;
}
interface Result {
  total: number;
  data: any[];
}

模拟真实出入参

如果你的真实出入参如下:

interface Params {
  pageSize: number;
  offset1: number;
  type: string;
  search: string;
}
interface Result {
  count: number;
  data: any[];
}

那你可以这样使用

<LoadMoreListView requestParams={{ type: 'alita', search: 'some search key', }}
alias={{ total: 'count', offset: 'offset1' }} />

requestFunc

请求执行函数接收的是一个异步函数,你可以把之前写在 @/services/api 中的函数传进去就可以。

import { query } from '@/services/api';
const Page = () => {
  return <LoadMoreListView requestFunc={query} />;
};
export default Page;

这样的好处是,依旧享受之前项目中的代理,前缀和请求中间件。

手动 reload 列表

一般用于请求参数变更之后,手动刷新请求。

import React, { FC, useState, useRef } from 'react';
import LoadMoreListView, { LoadMoreListAttributes } from '@alitajs/list-view';
 
const Page = () => {
  const loadMoreList = useRef<LoadMoreListAttributes>(null);
 
  return (
    <LoadMoreListView
      ref={loadMoreList}
      onClick={() => loadMoreList.current.reloadDataSource()}
    />
  );
};
export default Page;

购物车类型的 LoadMoreListView

保留 LoadMoreListView 的所有配置,通过修改 renderCartRow 将已选多选选中未选中的逻辑封装起来,便于使用。

请注意,为了防止误写和认知差异 renderRow 属性会被弃用。

参数说明类型默认值是否必填
renderCartRow定义渲染每行的方式(rowData: any,isSelected: boolean,selectItem: (key: any) => void,unSelectItem: (key: any) => void,) => React.ReactElement
onSelectChange选中数据变更时,回调(selectData: any, isSelectAll: boolean) => void
参数说明类型
rowData用于渲染每行的数据any
isSelected当前行是否被选中boolean
selectItem选中当前行(item: any) => void
unSelectItem取消选中当前行(item: any) => void

可以用上面的参数来渲染页面,如

const renderRow = (
  rowData: any,
  isSelected: boolean,
  selectItem: (key: any) => void,
  unSelectItem: (key: any) => void,
) => (
  <Item
    onClick={() => {
      if (isSelected) {
        unSelectItem(rowData);
      } else {
        selectItem(rowData);
      }
    }}
  >
    {isSelected ? '已选中' : '未选中'}
  </Item>
);
return <CartListView renderCartRow={renderCartRow} />;

由于《是否全选》是展示在父级,全选标示的逻辑在子组件内,因此你应该通过 onSelectChange 方法取得是否全选状态

import React, { FC, useState, useRef, useEffect } from 'react';
import { Button } from 'antd-mobile';
import { CartListView, CartListAttributes } from '@alitajs/list-view';
 
const [selectAll, setSelectAll] = useState(false);
const cartList = useRef<CartListAttributes>(null);
 
return (
  <>
    <Button
      onClick={() => {
        cartList.current.toggleAll();
      }}
    >
      {selectAll ? '已全选' : '未全选'}
    </Button>
    <CartListView
      ref={cartList}
      onSelectChange={(selectData: any, isSelectAll: boolean) => {
        setSelectAll(isSelectAll);
      }}
    />
  </>
);

所有支持的 current 方法

所有的方法都需要在异步方法中调用,因为在渲染函数中 current 未完全绑定。

export interface CartListAttributes {
  toggleAll: () => void;
  getSelectAll: () => boolean;
  selectItem: (item: any) => void;
  unSelectItem: (item: any) => void;
  getListData: () => any[];
  getSelectDate: () => any[];
  reloadDataSource: () => void;
}
参数说明
toggleAll切换全选选中状态,在 true 或 false 之间变更
getSelectAll获取当前的 全选状态
selectItem动态的选中某一项,item 为 rowData
unSelectItem动态的取消选中某一项,item 为 rowData
getListData获取当前列表中的值,与 onChange 中取到的值一致
getSelectDate获取当前列表中被选中的值,与 onSelectChange 中取到的值一致
reloadDataSource手动刷新列表数据,从初始值开始重新请求

更新日志

1.0.1-beta

  1. 替换@umijs/hooks(已废弃)为@ahooks。
  2. 优化上拉刷新动画、加载更多动画。
  3. 增加分页加载起始页startPage入参
  4. 修改 CartListView。

0.3.7

解决首屏加载条目不能填充listview容器时,无法触发加载更多

常见问题

  1. 当服务端数据返回格式多层嵌套时,如何设置alias?

    答: 目前alias是针对同层级结构。为了满足组件使用,需要你在 server 层转换数据格式。示例如下:

    // 服务端数据
    {
      resultCode: 0,
      resultMsg: '',
      resultObject: {
        total: 100,
        data: [
          { key: 0, label: "测试" },
          { key: 1, label: "测试1" },
          { key: 2, label: "测试2" }
        ],
        ...
      }
    }
     
    // 需要在Promise函数中转化
    const call = async (params) => {
      return request(params).then(res => ({ data: res.resultObject.data, total: res.total }));
    }
     
     // 使用示例
    <LoadMoreListView
       autoFullViewPort
       initialListSize={25}
       ref={loadMoreList}
       requestFunc={loadmore}
       renderRow={row}
       // 在这里调用Promise函数
       requestParams={call}
       startPage={10}
       alias={{
         offset: 'abc',
         pageSize: 'pagesize',
         page: 'pageNum',
         data: 'a',
         total: 't',
       }}
       noData={<div style={{ height: '100px', backgroundColor: '#f40' }}>123456</div>}
     />