Closed soly2014 closed 3 years ago
Will this help? I don't think I fully understand your intention. The screenshot posted is from youtube.
@hyochan what i mean is using Flatlist like this lib https://github.com/iftechio/react-native-virtualized-masonry
I think the point made by @soly2014 is a valid discussion. This issue was closed prematurely @hyochan
The documentation states the following:
Pinterest like listview made in React Native. It just behaves like the FlatList so it is easy to use.
I do not believe that statement to be entirely true: This module may have an api similar to that of a FlatList, but fundamentally, it behaves like a scrollview.
From the react native documentation:
Although this module may allow for infinite scrolling, as you keep scrolling, your data list will grow and eventually use up a lot of memory, especially if you are rendering images...
Supporting "windowing" could be a solution without needing to refactor the whole library. Users could then do something like:
<MasonryList
data={data.slice(offset, offset + 10)}
{...}
onEndReached={(): void => {
setOffset(offset + 10);
}}
/>
Unfortunately this is not supported at this time.
hi all, i was experiencing some heavy lag on Android when using the MasonryList
with a large dataset, or when there are multiple lists that were rendered (some on out of focus screens), and was looking for a virtualized version, but have not found one that fits my use.
as a result, i've made some changes to this component, shown in the following patch.
more of a work in progress, only works for vertical atm (only using vertical in my app right now)
thanks!
bit of an explanation:
items
refoffset
refdiff --git a/node_modules/@react-native-seoul/masonry-list/lib/index.d.ts b/node_modules/@react-native-seoul/masonry-list/lib/index.d.ts
index 3458b95..effa73b 100644
--- a/node_modules/@react-native-seoul/masonry-list/lib/index.d.ts
+++ b/node_modules/@react-native-seoul/masonry-list/lib/index.d.ts
@@ -22,6 +22,9 @@ interface Props<T> extends Omit<ScrollViewProps, 'refreshControl'> {
containerStyle?: StyleProp<ViewStyle>;
numColumns?: number;
keyExtractor?: ((item: T | any, index: number) => string) | undefined;
+ refreshOffsetInterval?: number;
+ windowOffset?: number; // to workaround empty space rendered when scrolling up
+ gutterWidth?: number; // width between columns ... only for vertical currently
}
declare function MasonryList<T>(props: Props<T>): ReactElement;
declare const _default: React.MemoExoticComponent<typeof MasonryList>;
diff --git a/node_modules/@react-native-seoul/masonry-list/lib/index.js b/node_modules/@react-native-seoul/masonry-list/lib/index.js
index a70f495..f8f0f6a 100644
--- a/node_modules/@react-native-seoul/masonry-list/lib/index.js
+++ b/node_modules/@react-native-seoul/masonry-list/lib/index.js
@@ -9,8 +9,8 @@ var __rest = (this && this.__rest) || function (s, e) {
}
return t;
};
-import { RefreshControl, ScrollView, View, } from 'react-native';
-import React, { memo, useState } from 'react';
+import { RefreshControl, ScrollView, View, Dimensions} from 'react-native';
+import React, { memo, useState, useRef, useEffect } from 'react';
const isCloseToBottom = ({ layoutMeasurement, contentOffset, contentSize }, onEndReachedThreshold) => {
const paddingToBottom = contentSize.height * onEndReachedThreshold;
return (layoutMeasurement.height + contentOffset.y >=
@@ -18,14 +18,60 @@ const isCloseToBottom = ({ layoutMeasurement, contentOffset, contentSize }, onEn
};
function MasonryList(props) {
const [isRefreshing, setIsRefreshing] = useState(false);
- const { refreshing, data, innerRef, ListHeaderComponent, ListEmptyComponent, ListFooterComponent, ListHeaderComponentStyle, containerStyle, contentContainerStyle, renderItem, onEndReachedThreshold, onEndReached, onRefresh, loading, LoadingView, numColumns = 2, horizontal, onScroll, removeClippedSubviews = false, keyExtractor, } = props;
+ const { refreshing, data, innerRef, ListHeaderComponent, ListEmptyComponent, ListFooterComponent, ListHeaderComponentStyle, containerStyle, contentContainerStyle, renderItem, onEndReachedThreshold, onEndReached, onRefresh, loading, LoadingView, numColumns = 2, horizontal, onScroll, removeClippedSubviews = false, keyExtractor, refreshOffsetInterval=500, windowOffset=0, gutterWidth=0 } = props;
const { style } = props, propsWithoutStyle = __rest(props, ["style"]);
+ const offset = useRef(0);
+ const [refreshOffset, setRefreshOffset] = useState(0);
+ const items = useRef({});
+
+ const _renderItem = (col, i, el) => {
+ const outOfView =
+ ((items?.current?.[col]?.[i]?.offset ?? 99999) <
+ (offset?.current ?? 0) - Dimensions.get("window").height - windowOffset)
+ ||
+ ((items?.current?.[col]?.[i]?.offset ?? 0) >
+ (offset?.current ?? 0) + Dimensions.get("window").height)
+ if (outOfView) {
+ return (
+ <View style={{ height: items?.current?.[col]?.[i]?.height ?? 100 }} key={`masonry-empty-spacer-${col}-${i}`}/>
+ );
+ }
+ return (
+ <View
+ key={keyExtractor === null || keyExtractor === void 0 ? void 0 : keyExtractor(el, i)}
+ onLayout={({ nativeEvent }) => {
+ const itemsCopy = items?.current ?? {};
+ const itemsColumn = itemsCopy[col] ?? {};
+ itemsColumn[i] = {
+ offset: nativeEvent?.layout?.y ?? 0,
+ height: nativeEvent?.layout?.height ?? 0,
+ };
+ items.current = { ...items?.current, [col]: itemsColumn };
+ }}
+ >
+ {renderItem({ item: el, i })}
+ </View>
+ );
+ };
+
+ useEffect(() => {
+ return () => {
+ offset.current = 0;
+ items.current = {};
+ };
+ }, []);
+
return (<ScrollView {...propsWithoutStyle} ref={innerRef} style={[{ flex: 1, alignSelf: 'stretch' }, containerStyle]} contentContainerStyle={contentContainerStyle} removeClippedSubviews={removeClippedSubviews} refreshControl={<RefreshControl refreshing={!!(refreshing || isRefreshing)} onRefresh={() => {
setIsRefreshing(true);
onRefresh === null || onRefresh === void 0 ? void 0 : onRefresh();
setIsRefreshing(false);
}}/>} scrollEventThrottle={16} onScroll={(e) => {
const nativeEvent = e.nativeEvent;
+ const offsetCurrent = nativeEvent?.contentOffset?.y ?? 0;
+ offset.current = offsetCurrent;
+ if (Math.abs(offsetCurrent - refreshOffset) > refreshOffsetInterval) {
+ setRefreshOffset(offsetCurrent);
+ }
if (isCloseToBottom(nativeEvent, onEndReachedThreshold || 0.1))
onEndReached === null || onEndReached === void 0 ? void 0 : onEndReached();
onScroll === null || onScroll === void 0 ? void 0 : onScroll(e);
@@ -42,13 +88,16 @@ function MasonryList(props) {
return (<View key={`masonry-column-${num}`} style={{
flex: 1 / numColumns,
flexDirection: horizontal ? 'row' : 'column',
+ paddingRight:(num < numColumns-1) ? gutterWidth : 0
+ // paddingRight:16
}}>
{data
.map((el, i) => {
if (i % numColumns === num)
- return (<View key={keyExtractor === null || keyExtractor === void 0 ? void 0 : keyExtractor(el, i)}>
- {renderItem({ item: el, i })}
- </View>);
+ // return (<View key={keyExtractor === null || keyExtractor === void 0 ? void 0 : keyExtractor(el, i)}>
+ return _renderItem(num, i, el)
+ {/* {renderItem({ item: el, i })} */}
+ // </View>);
return null;
})
.filter((e) => !!e)}
Hey @j-cheung Better would be if you can fork out the library and provide this patch. I think this is a much needed thing if we have to maintain performance
Hey @j-cheung Better would be if you can fork out the library and provide this patch. I think this is a much needed thing if we have to maintain performance
hi @iarmankhan, i'll look into it when i'm free, feel free to do it as well. i only did the patch because i was mostly focused on trying to get the change on my production app asap. thanks.
Is your feature request related to a problem? Please describe. A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
Describe the solution you'd like A clear and concise description of what you want to happen.
Describe alternatives you've considered A clear and concise description of any alternative solutions or features you've considered.
Additional context Add any other context or screenshots about the feature request here.