一个好用的小组件:列表懒加载

792 阅读3分钟

这是我参与「掘金日新计划 · 4 月更文挑战」的第14天,点击查看活动详情

大家好,我是小杜杜,在移动端,我们常常会用懒加载去处理数据,这是接口通常会弄分页,前端需要传入不同的页数(page) 和 一页多少条数据(pageSize) 来掉取数据,然后依次展示即可~

条件

当你需要 一个分页的接口,展示列表的数据,需要懒加载,但不想麻烦处理接口,处理数据、处理样式的时候。 可以尝试用用这个组件

大体思路

在这里我们需要使用 ant-mobileinfiniteScroll这个组件,我们在此基础上结合接口封装,达到列表懒加载

先说说没有这个组件时候,我通常是如何处理的:在用户拉动列表到底部的时候,监听滚动条的高度,当可视区域的高度 + 滚动条滚动的高度 > 滚动内容高度 的时候做判断就OK了

  const onScroll = () => {
    let clientHeight = scrollRef.current.clientHeight; //可视区域高度
    let scrollTop  = scrollRef.current.scrollTop;  //滚动条滚动高度
    let scrollHeight = scrollRef.current.scrollHeight; //滚动内容高度
    if((clientHeight+scrollTop + 100) >(scrollHeight) && curPage <= allNumber){
    	......
    }
  }

<div onScroll={onScroll} ref={scrollRef}>

这里最好多加入点像素,以防有些时候触发不了,题外话~~~

怎样将 infiniteScroll 与 接口结合起来,形成 ScrollList 组件

首先关于 infiniteScroll 的 Api 非常简单,只有简单的三个属性

  • loadMore: 加载更多的回调函数
  • hasMore: 是否还有更多内容
  • threshold: 触发加载事件的滚动触底距离阈值,单位为像素

我可以看到,最主要的就是 loadMore 这个参数,他是这个组建的核心,所以需要围绕这个属性去修改

我们通过传参来选择接口返回的数据,然后将数据累加,并判断数据的合 是否等于总数据,然后将数据返回出去,这是返回的data就可循环做一写操作,通过 childrenNode返回节点就ok了

同时也可以通过 LoadingNode来控制 加载时的样式 NoneNode来控制加载完成时的样式

代码案例

image.png

使用方法


    import { useState, useEffect } from 'react';
    import { ScrollList } from '@/components';
    import { List } from 'antd-mobile'
    import { Title } from '@/pages/commonPage'
    import { getScrollList } from './services'

    const Index = () => {

      useEffect(() => {
      }, [])

      return (
        <div style={{ padding: '6px 12px'}}>
          <ScrollList
            onRequest={getScrollList}
            payload={{
              sizeName: 'size',
              sizeNumber: 20
            }}
            childrenNode={(data:any) => {
              return <>
                <div style={{ padding: 12 }}>展示数量/总数量:{data.list.length}/{data.res.all}</div>
                <List>
                  {
                  data.list.map((item:any, index:any) => (
                    <List.Item key={index}>{item.name}</List.Item>
                  ))
                }
              </List>
              </>
            }}
          >
          </ScrollList>
        </div>
      )
    }

    export default Index;

详细代码

    import React, { useState } from 'react';
    import { InfiniteScroll, Loading } from 'antd-mobile'
    import { IndexProps } from './interface.d'
    import { useReactive } from 'ahooks';

    /**
     * @module 无限滚动,列表懒加载
     */

    function setWait(number:number) {
      return new Promise((resolve:any) => {
        setTimeout(() => {
          resolve();
        }, number);
      });
    }

    const Index:React.FC<IndexProps> = ({ onRequest, payload, calcData, childrenNode, LoadingNode, NoneNode, threshold = 250, wait = 1000}) => {

      let [node, setNode] = useState<React.ReactNode>(<></>)

      const state = useReactive<any>({
        hasMore: true,
        page: 1,
        number: 0,
        data: []
      })

      const loadMore = async () => {
        await setWait(wait)
        let params:any = {}
        params[payload?.pageName || 'page'] = state.page;
        params[payload?.sizeName || 'pageSize'] = payload?.sizeNumber || 10;
        const res = await onRequest(calcData ? { ...calcData(), ...payload} : params)
        const number = state.number + res[payload?.list || 'list'].length;
        const data = [...state.data, ...res[payload?.list || 'list']]
        delete res[payload?.list || 'list']
        setNode(childrenNode({ list: data, res }))
        state.hasMore = false
        state.number = number
        state.data = data
        if(res[payload?.all || 'all'] <= number){
          state.hasMore = false
        }else{
          state.hasMore = true
          state.page++
        }
      }

      const InfiniteScrollContent = ({ hasMore }: { hasMore?: boolean }) => {
        return (
          <>
            {hasMore ? LoadingNode ? (<>{LoadingNode}</>) : (
              <>
                <span>加载中</span>
                <Loading />
              </>
            ) : NoneNode ? (<>{NoneNode}</>) : (
              <span>--- 我是有底线的 ---</span>
            )}
          </>
        )
      }

      return (
        <>
          {node}
          <InfiniteScroll loadMore={loadMore} hasMore={state.hasMore} threshold={threshold}>
            <InfiniteScrollContent hasMore={state.hasMore} />
          </InfiniteScroll>
        </>
      );
    }

    export default Index;

End

将请求和处理函数封装在一起,可以有效地进行开发,进行二次封装更好的适配于项目,喜欢的小伙伴点个 Star 支持下吧~