react-native-staggered-list v3.0.1
react-native-staggered-list
基于 VirtualizedList 封装的 react-native 瀑布流组件。
经过无数次迭代,目前包里面有三个版本 deprecated、default 和 withDimensions。
这里面最开始的版本是可以自动测量高度的 deprecated,后来做了一半比较稳定的 default,而且经过线上验证了。最近这一版做的是 withDimensions。
主要区别如下:
| Deprecated | Default | withDimensions | |
|---|---|---|---|
| 环境等级 | 测试 Dev | 生产 Pro | 生产 Pro |
| 排列方式 | 最短列填充 | 从左到右 | 最短列填充 |
| 自动计算高度 | ✅ | x | x |
| 分页 | x | ✅ | ✅ |
| 滚动到指定位置 | x | ✅ | ✅ |
业务场景:
实际上数据源
data都是运营的同学负责添加的,现在的APP基本上滑动不到底部,因为数据量实在很大。所以就算是几个列的高度差别很大,基本也不影响。这也是我决定最后一次改版的主要原因。用户体验:
- 之前的版本都是动态计算,那就必然得等前一个渲染完了,才能渲染下一个。还是之前说的问题,会像 鸡
🐔下蛋🥚一样,一个个的往外蹦。 - 新版本我加入了动画,交互效果看起来能好一些。
- 之前的版本都是动态计算,那就必然得等前一个渲染完了,才能渲染下一个。还是之前说的问题,会像 鸡
虽然说
😍 组件特色
🌽 智能排列
ScrollView→VirtualizedList。- 经过这么多期不断优化迭代,可见部分采用
从左到右依次填充,不可见部分采用高度最小列优先填充。
🍉 泛型支持
像 FlatList 一样 renderItem,然后支持自己的 ItemT。
🍇 扩展性强
支持自定义列数
columns。支持下拉刷新
onRefresh()。支持自定义
Header和Footer。支持自定义列表的
Container样式。支持滑动监听
onScroll(NativeSyntheticEvent<NativeScrollEvent>) => void。支持分页加载,加载完了一页回调
onCompleted: () => void,接着来下一页的数据也是OJBK的。
觉得有用,路过的各位老铁们右上角的小星星走起来,谢谢。

😌 命名规范
整体的设计思想模仿的是 FlatList,提供以下内容的自定义。
| Name | Type | Description |
|---|---|---|
| columns | number | 列数。 |
| datas | ItemT [] | 数据源 ( 支持泛型 ItemT )。 |
| renderItem | (item: ItemT) => React.Node | 跟 FlatList 一样,渲染什么您说了算。 |
| onLoadComplete | () => void | 数据全部渲染完成时候的回调,比如 分页 这种应用场景。 |
| header | React.Node | 瀑布流的头部。 |
| footer | React.Node | 瀑布流的尾部。 |
| showsVerticalScrollIndicator | boolean | 是否显示 纵向 滚动条。 |
| onScroll | (NativeSyntheticEvent<NativeScrollEvent>) => void | 滑动事件,比如 吸顶 要判断滑动距离这种场景。 |
| onRefresh | () => void | 下拉刷新时候的回调。 |
| columnsStyle | StyleProp<ViewStyle> | 瀑布流的 Container 的样式,可以控制 内边距 以及 列表 和 Header 的距离等。 |
🤔 如何使用
npm install react-native-staggered-listimport {
Waterfall,
WaterfallWithDimensions,
} from "react-native-staggered-list";<Waterfall
onRefresh={() => {
setR(Math.random());
setPageIndex(1);
}}
header={<View />}
datas={datas}
renderItem={(item) => <HomeItem item={item} />}
columns={2}
columnsStyle={{
justifyContent: "space-around",
paddingHorizontal: 5 * vw,
}}
onScroll={(e) => {
setTabBarOpacity(Math.min(1, e.nativeEvent.contentOffset.y / imgHeight));
}}
onLoadComplete={() => {
setPageIndex((t) => t + 1);
}}
/>😳 实现原理
两种思路:
Waterafall
直接挨个 index%column 往里面填充,适合左右两边高度差不多相等的情况。
WaterfallWithDimensions
需要在数据源中加入 dimensions: {width: number, height: number},然后根据每一列的高度,填充最低的高度。
Deprecated
不推荐,有很多缺陷。
Item会不断的onLayout()还会有硬件方面性能的损失,再就是就算是拿到renderItem里面的状态的话,那也是像老母鸡 🐔 下蛋 🥚 一样,一个一个的渲染,体验上也说不过去。Item的onLayout()其实并不是预期的那样,他会立即执行一次或者两次,而不是布局变化的时候进行回调。那么我就要在renderItem()里面做文章,但是Item好像拿不到props.children里面的状态,这就很麻烦。想了很多方法,感觉都不是很好。
代码贴出来:
const Item: React.FC<ItemProps> = (props) => {
return (
<View
onLayout={(layout) => {
// console.log(layout.nativeEvent.layout);
layout.nativeEvent.layout.height > 0 &&
props.onMeasuredHeight(layout.nativeEvent.layout.height);
}}
>
{React.isValidElement(props.children) &&
React.cloneElement(props.children, {
nextRender: (next: boolean, height: number) => {
next && props.onMeasuredHeight(height);
},
})}
{/* {props.children} */}
</View>
);
};之前想了个办法,刚开始肉眼可见的区域是直接从左到右依次填充。给了一个高度容错的范围,默认 [0, 2*props.columns]。在这个范围里面的数据,渲染的时候,延时 1000ms,这样儿确保了前面的数据渲染完了,拿到的高度能更真实一些。也就是说最后这几个 Item 是优化布局,纠错用的。
但是这样儿分页又出问题了。有可能这一页还没渲染完,这个时候如果用户下拉刷新,就会导致组件内的 index 出问题。
所以这里我把 Deprecated 代码提供出来了,起码让各位读者了解我在封装这个组件时候的思路,但是不推荐使用。
🙄 版本记录
🚀 Version 3.0
🚀 Publish Waterfall & WaterfallWithDimensions。
🚀 Version 2.0.0
新版本组件 命名方式 和 参数 与 FlatList 一模一样。需要动态计算高度的,请自行安装 1.x 的最后一个版本 1.9.0。
新版组件主要考虑到了 业务场景 和 用户体验 方面,只采用 从左到右 依次渲染。
业务场景: 实际上数据源
data都是运营的同学负责添加的,现在的APP基本上滑动不到底部,因为数据量实在很大。所以就算是几个列的高度差别很大,基本也不影响。这也是我决定最后一次改版的主要原因。用户体验:
- 之前的版本都是动态计算,那就必然得等前一个渲染完了,才能渲染下一个。还是之前说的问题,会像 鸡
🐔下蛋🥚一样,一个个的往外蹦。 - 新版本我加入了动画,交互效果看起来能好一些。
- 之前的版本都是动态计算,那就必然得等前一个渲染完了,才能渲染下一个。还是之前说的问题,会像 鸡
🍀 Version 1.0.0
🍀 Published react-native-staggered-list,支持分页加载 & Header & Footer 等功能。
- Version 1.0.1
- 🗑 删除多余依赖。
- ✍🏻 重命名
StaggeredListView→StaggeredList。 - 🛠 更新 README.md。
Version 1.1.0
🆕 新增原生滑动事件的回调:
onScroll: (NativeSyntheticEvent<NativeScrollEvent>) => void。🆕 新增 Header & Columns & Footer 测量高度的回调。
有了以上这两个事件,就可以在使用的时候,实现
TabBar的渐变以及吸顶效果。Version 1.1.1
- 🐞 修改初始化
measureResult,防止header或者footer为null造成的回调参数为空的 BUG。
- 🐞 修改初始化
- Version 1.2.0
- 🆕 新增下拉刷新功能
onrefresh: () => void。 - 🛠 更新 README.md,添加运行截图,以及示例代码。
- 🆕 新增下拉刷新功能
- Version 1.2.1
- 🛠 修改 README.md。
- Version 1.3.0
- 🆕 新增
Columns样式自定义,可以自己调节Header和Columns之间的距离,也可以自己调节Columns和屏幕两边的边距。
- 🆕 新增
- Version 1.4.0
- 🗑 移除原来除了测量除了
header和footer测量的逻辑,直接从左到右每一列挨个填充Item。
- 🗑 移除原来除了测量除了
- Version 1.4.1
- 🐞 潜藏的 BUG。
- Version 1.4.2
- 🐞 瀑布流渲染的错误。
- Version 1.5.0
- 🚀 综合
从左到右依次填充和最小高度填充两种方式,使瀑布流两边高度尽量一致。 - 🆕 新增
List→Item右下角的index,便于直观的看到渲染顺序和效果。
- 🚀 综合
- Version 1.5.1
- 🐞。
Version 1.6.0
- 🚀 全新升级: 最外层由
ScrollView→VirtualizedList,包括内层的View堆砌也换成了VirtualizedList。而且还解决了一些奇怪的问题,比如之前遇见过把Banner放到Header里面无法自动轮播,必须要手动碰一下才可以。
- 🚀 全新升级: 最外层由
Version 1.6.1
- 🗑 删除
Header以及Footer的测量的回调。
- 🗑 删除
- Version 1.6.2
- 🐞
RefreshControl报错。
- 🐞
Version 1.7.0
- 🐞 新增下拉刷新的防抖的处理,防止用户不断下拉刷新造成重复渲染的 BUG。
- 💄 优化瀑布流排列,不可见区域采用延时处理,排列更为准确。
Version 1.7.1
- 🐞 修改了一下防抖的时机,改为
onRefresh()回调前就进行处理。
- 🐞 修改了一下防抖的时机,改为
- Version 1.7.2
- 🐞 还是防抖的逻辑,不要控制
refreshing,控制r→setR(Math.random())。
- 🐞 还是防抖的逻辑,不要控制
- Version 1.7.3
- 🛠 更新 README.md。
- Version 1.8.0
- 🆕 新增泛型
ItemT的支持。
- 🆕 新增泛型
- Version 1.8.1
- 🛠 修改 README.md。
Version 1.9.0
🐞 修改加载完成
onLoadComplete()的逻辑,因为当数据量比较大的时候,即使你不滑动,他也会不断onLoadComplete()。- 资源的浪费,不断加载下一页,会导致服务端的压力也变大。
- 下拉刷新有
BUG,为了让每一列能得到比较准确的高度,我会在添加的时候,加一个计时器,如果他在不断的渲染的过程中,你突然下拉刷新,状态不好控制,会有意想不到的BUG出现。
- 下拉刷新有
所以这次更新,我回调的逻辑是 每一列都滑动到底部了,并且 数据渲染完了,这个时候我再去回调。
3 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago