关于react.js:React列表组件通知列表私信列表虚拟列表

引言

最近在做社交网站开发,过程中须要用到三种组件:告诉列表组件、聊天列表组件和虚构列表组件。这三种组件都是社交网站必备的,当初把我在开发中遇到的问题以及代码全副分享给大家,心愿对大家有所帮忙。

告诉列表

咱们在应用社交网站的过程中会发现他们通常会应用下拉告诉展示告诉列表数据,这种就是当初要介绍的告诉列表,如图所示:

首先咱们先来介绍下告诉下拉列表的工作原理:
1、点击告诉按钮
2、查问列表数据并展示
3、向下拉滚动条过程中分页加载数据,并把数据合并到列表中,直到数据全副加载完

当初看看咱们如何制作吧。首先,咱们须要应用一个React插件:InfiniteScroll,废话不多说间接上代码:

<div id="scrollableDiv" className={styles.noticeBody}>
              {
                loading&&page.pageNum === 1? <Loading />:<InfiniteScroll
                  //留神:dataLength={remindList.length}要写remindList.length不能写成remindListTotal,切记!
                    dataLength={remindList.length}
                    next={loadMoreData}
                    height={413}
                    hasMore={remindList.length < remindListTotal}
                    loader={<Loading/>}
                    scrollableTarget="scrollableDiv">
                  <List
                      split={false}
                      itemLayout="horizontal"
                      dataSource={remindList}
                      renderItem={item => (your list item in here)}
                  />
                </InfiniteScroll>
              }
            </div>

咱们看一下下面的代码,首先咱们要定义一个id=scrollableDiv的div,接着判断如果以后页码是1的话,则显示loading加载组件。

留神: 因为InfiniteScroll组件,默认如果没有数据是不被动触发next对应的loadMoreData获取下一页数据的办法,所以最好咱们关上下拉框的时候就被动去获取列表第一页的数据,在获取过程中咱们能够先用loading成果展现给用户,目标是为了晋升更好的体验,当然你也能够不必加!

loading&&page.pageNum === 1? <Loading />

当初,来看看这段代码:

<InfiniteScroll
                  //留神:dataLength={remindList.length}要写remindList.length不能写成remindListTotal,切记!
                    dataLength={remindList.length}
                    next={loadMoreData}
                    height={413}
                    hasMore={remindList.length < remindListTotal}
                    loader={<Loading/>}
                    scrollableTarget="scrollableDiv">
                  <List
                      split={false}
                      itemLayout="horizontal"
                      dataSource={remindList}
                      renderItem={item => (your list item in here)}
                  />
                </InfiniteScroll>

不晓得大家有没有看InfiniteScroll文档下面的代码,如果没有的话,我这里就简略介绍一下,并且把开发过程中遇到的问题给大家点明一下避免入坑。
dataLength={remindList.length}示意以后数据长度
next={loadMoreData}示意获取下一页数据的办法,它会随着滚动条的滚动主动触发的
hasMore={remindList.length < remindListTotal}示意什么时候显示loading成果
loader={<Loading/>}示意loading成果组件
scrollableTarget示意它是依赖于id=scrollableTarget的div的

留神:1、这里须要留神的是dataLength应该是以后列表的长度,否则滚动条滚动到列表底部的时候不会触发获取下一页数据的办法loadMoreData
2、因为下拉列表滚动加载过程中,列表数据源remindList是始终减少的,它是把每页的数据源merge在一起的。

私信列表

私信列表就比拟非凡了,大家都用过微信,QQ的,它的聊天记录是向上滚动加载,跟咱们的告诉下拉列表刚好相同,庆幸的是InfiniteScroll组件也提供该性能,间接上代码:

<div className={styles.chatList} id="scrollableDiv" ref={dom}>
            <InfiniteScroll
              //留神:dataLength={remindList.length}要写remindList.length不能写成remindListTotal,切记!
              dataLength={chatList.length}
              next={getCurrentData}
              hasMore={showChatListLoading}
              loader={<Loading/>}
              style={{
                display: "flex",
                flexDirection: "column-reverse"
              }}
              scrollableTarget="scrollableDiv"
              inverse={true}>
                your list item in here
            </InfiniteScroll>
          </div>

跟之前一样,咱们来剖析下这段代码,因为是滚动条向上滚动加载,所以咱们要把loading搁置在顶部,所以要加上inverse={true},同时还要设置两个款式:
款式一、

style={{
    display: "flex",
    flexDirection: "column-reverse"
  }}

款式二、

.chatList{
  height: calc(100vh - 186px);
  overflow-y: auto;
  display: flex;
  flex-direction: column-reverse;
  overflow-anchor: none;
}

这样就实现了反向上拉加载分页数据了,其它属性跟下面大同小异这里就不过多形容了,不过要留神几个问题:

留神:1、我在向上滚动的时候分页也胜利了,也合并到列表中,可是滚动条始终在顶部,看过qq和微信的同学应该都晓得,向上滚动加载的时候,滚动条应该在以后聊天记录上,而不是在最顶部,而后在网上搜寻才晓得,只有在父节点上加上这个代码就能够了:overflow-anchor: none;
2、聊天记录的列表跟下拉告诉列表数据也是类似的,每次向上滚动的时候咱们都会合并到chatList(暂定为聊天记录列表名)中,然而有一点不一样,我能够通过输出聊天内容并展示到列表中,告诉下拉可是没有输出展示性能,这点十分重要,因为咱们会遇到一个十分辣手的问题:如何正确合并数据?以及合并数据之后分页查问反复问题?
正确合并数据:预计好多小伙伴曾经想到了,后盾把数据推送给前端之后,间接concat到chatList中(留神不是分页查问,因为那样页面会有闪动的不好体验),这也没问题
分页查问反复问题:咱们来看看什么是分页查问反复问题,这里有篇文章大家能够看看,于是咱们应用了下面第2种解决方案。

解决分页查问反复问题

解决思路2
申请第1页时记录第1条数据(即最新的那条)的写入工夫, 而后前面查问第2,3,4…页数据, 把记录的写入工夫作为参数, 而后在sql语句中做限度
例如查问第2页, 设置写入工夫小于等于2019-05-15 19:31:59, 这样即便有新数据插入, 也不在咱们本次分页查问的范畴内.
select * from table1 where write_time <=1557919919000 order by write_time desc limit 5,5

既然咱们曾经晓得了如何解决,上面给出具体步骤以及代码:

1、当后盾推送数据给前端的时候,咱们先把数据合并到chatList中,并给个标识type=websocket
2、当用户向上滚动的时候,咱们能够通过findIndex拿到这个type=websocket的数据的创立工夫,通过分页接口传递给后盾
3、后盾返回数据之后咱们再合并到chatList中

分页代码:

export const getMessages = createAsyncThunk('notify/getMessages', async (params, thunkAPI) => {
  try {
    const notify = thunkAPI.getState().notify
    if (notify.chatListPage.pageNum > 1) {
      // 找到第1个type=websocket的数据,而后赋值给flagCreatedTime即可
      // 为什么找到第1个?因为list中新加的websocket数据是从尾部开始加的,所以只有从索引0找到到最近一个type=websocket就是从这个工夫开始算的,而不是最初一个
      const i = notify.chatList.findIndex(item => item.type === 'websocket')
      if (i > -1) {
        params.flagCreatedTime = notify.chatList[i].createdDt
      }
    }


    const res = await axios.post(`/notify/crud/messages/getMessages`, {
      dialogId: params.dialogId,
      pageNum: params.pageNum,
      pageSize: params.pageSize,
      flagCreatedTime: params.flagCreatedTime
    });
    return res.data
  } catch (error) {
    return thunkAPI.rejectWithValue({errorMsg: error.message});
  }
});

分页办法的回调:

[getMessages.fulfilled]:(state, action) => {
    if (action.payload.data) {
      // 获取总数
      const total = action.payload.data.total
      state.chatListPage = {
        pageSize: action.payload.data.pageSize,
        pageNum: action.payload.data.pageNum,
        total: action.payload.data.total
      }

      const rows = action.payload.data.rows.reverse()

      // 这里做这个判断是因为react18 useeffect会执行两次,所以我依据Pagenum判断是否要合并以防止反复合并问题
      if (action.payload.data.pageNum > 2) {
        state.chatList = [
          ...rows,
          ...state.chatList
        ]
      } else {
        state.chatList = rows
      }
      // 设置是否要分页加载,InfiniteScroll组件的hasMore属性会用到

      state.showChatListLoading = state.chatList.length < total
        // 当pageNum是第1页并且总数据还不到pageSize,阐明基本没分页的必要
        && !(total<state.chatListPage.pageSize && state.chatListPage.pageNum === 1)
      // state.loadingMsg = false;
    }
  },

合并的代码:

// 接管音讯并合并到chatList音讯列表中
concatMessageInChatList: (state, action) => {
  if (action.payload.length > 0) {
    action.payload.forEach(item => {
      item.type = 'websocket';
    })
  }
  state.chatList = [
    ...state.chatList,
    ...action.payload
  ]
}

这样就能解决分页数据反复问题以及合并问题了。

虚构列表

总结

援用

【腾讯云】轻量 2核2G4M,首年65元

阿里云限时活动-云数据库 RDS MySQL  1核2G配置 1.88/月 速抢

本文由乐趣区整理发布,转载请注明出处,谢谢。

您可能还喜欢...

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据