Skip to content

Commit 723f97e

Browse files
committed
Added keyExtractor and onThreshold logic to FlexGrid and ResponsiveGrid components
1 parent c44b758 commit 723f97e

File tree

11 files changed

+447
-209
lines changed

11 files changed

+447
-209
lines changed

README.md

Lines changed: 73 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -77,12 +77,12 @@ import { FlexGrid } from 'react-native-flexible-grid';
7777

7878
export default function App() {
7979
const data = [
80-
{ imageUrl: 'https://picsum.photos/200/300?random=1', widthRatio: 1, heightRatio: 1, text: "Item 1" },
81-
{ imageUrl: 'https://picsum.photos/200/300?random=2', widthRatio: 2, heightRatio: 1, text: "Item 2" },
82-
{ imageUrl: 'https://picsum.photos/200/300?random=3', widthRatio: 2, heightRatio: 1, text: "Item 3" },
80+
{ imageUrl: 'https://picsum.photos/200/300?random=1', widthRatio: 1, heightRatio: 1, text: "Item 1", id: 1 },
81+
{ imageUrl: 'https://picsum.photos/200/300?random=2', widthRatio: 2, heightRatio: 1, text: "Item 2", id: 2 },
82+
{ imageUrl: 'https://picsum.photos/200/300?random=3', widthRatio: 2, heightRatio: 1, text: "Item 3", id: 3 },
8383
];
8484

85-
const renderItem = (item, index) => (
85+
const renderItem = ({item, index}) => (
8686
<View style={styles.boxContainer}>
8787
<Image
8888
source={{ uri: item.imageUrl }}
@@ -96,6 +96,7 @@ export default function App() {
9696
return (
9797
<View style={styles.container}>
9898
<FlexGrid
99+
keyExtractor={(item) => item.id.toString()}
99100
maxColumnRatioUnits={60}
100101
itemSizeUnit={60}
101102
data={data}
@@ -133,25 +134,25 @@ import { ResponsiveGrid } from 'react-native-flexible-grid';
133134

134135
export default function App() {
135136
const data = [
136-
{ imageUrl: 'https://picsum.photos/200/300?random=1', widthRatio: 1, heightRatio: 1, text: "Item 1" },
137-
{ imageUrl: 'https://picsum.photos/200/300?random=2', widthRatio: 2, heightRatio: 1, text: "Item 2" },
138-
{ imageUrl: 'https://picsum.photos/200/300?random=3', widthRatio: 2, heightRatio: 1, text: "Item 3" },
137+
{ imageUrl: 'https://picsum.photos/200/300?random=1', widthRatio: 1, heightRatio: 1, text: "Item 1", id: 1 },
138+
{ imageUrl: 'https://picsum.photos/200/300?random=2', widthRatio: 2, heightRatio: 1, text: "Item 2", id: 2 },
139+
{ imageUrl: 'https://picsum.photos/200/300?random=3', widthRatio: 2, heightRatio: 1, text: "Item 3", id: 3 },
139140
];
140141

141-
const renderItem = (item, index) => (
142+
const renderItem = ({item, index}) => (
142143
<View style={styles.boxContainer}>
143144
<Image
144145
source={{ uri: item.imageUrl }}
145146
style={styles.box}
146147
resizeMode="cover"
147148
/>
148-
<Text style={styles.text}>{item.text}</Text>
149149
</View>
150150
);
151151

152152
return (
153153
<View style={styles.container}>
154154
<ResponsiveGrid
155+
keyExtractor={(item) => item.id.toString()}
155156
maxItemsPerColumn={3}
156157
data={data}
157158
renderItem={renderItem}
@@ -197,8 +198,14 @@ export default function App() {
197198
<td><code>Function</code></td>
198199
<td><code> () => null</code></td>
199200
<td><code>true</code></td>
200-
<td>Its used for defining how each individual item should appear, it allows utilizing the item's properties to construct a custom layout or UI for that item within the grid.</td>
201+
<td>Defines how each individual item should appear, it allows utilizing the item's properties to construct a custom layout or UI for that item within the grid.</td>
201202
</tr>
203+
<td><code>keyExtractor</code></td>
204+
<td><code>(item, index) => string</code></td>
205+
<td><code>Function</code></td>
206+
<td><code>false</code></td>
207+
<td> Defines a function that extracts a unique key for a given item in the list. By default, the <code>keyExtractor</code> uses the item's index in the array as the key. This is crucial for optimizing the rendering and re-rendering of list items by providing a stable identity. The function receives an item from the data array and its index, and should return a unique string. Providing a custom <code>keyExtractor</code> is recommended when the items have a unique identifier other than the index, especially in cases where the list's order might change, or items are dynamically added or removed, to ensure consistent and efficient updates. </td>
208+
</tr>
202209
<tr>
203210
<td><code>maxColumnRatioUnits</code></td>
204211
<td><code>Number</code></td>
@@ -243,7 +250,7 @@ While, a lower buffer factor reduces the off-screen component count, optimizing
243250
<td><code>Number</code></td>
244251
<td><code>200</code></td>
245252
<td><code>false</code></td>
246-
<td>Specifies the interval, in milliseconds, at which scroll events are processed for the purpose of recalculating visible items and buffer in a virtualized grid. By setting this prop, developers can throttle the frequency of scroll event handling, optimizing performance during rapid scrolling by reducing the computational load associated with updating the list of visible items and buffer.
253+
<td>Defines the interval, in milliseconds, at which scroll events are processed for the purpose of recalculating visible items and buffer in a virtualized grid. By setting this prop, developers can throttle the frequency of scroll event handling, optimizing performance during rapid scrolling by reducing the computational load associated with updating the list of visible items and buffer.
247254

248255
A lower value results in more frequent updates, offering smoother visual updates of the grid's content at the potential cost of higher computational overhead. While, a higher interval decreases the frequency of updates, potentially improving performance but with less immediate recalculation triggered by scroll actions. This is crucial for fine-tuning the performance and experience of virtualized grid.
249256
</td>
@@ -269,6 +276,36 @@ A lower value results in more frequent updates, offering smoother visual updates
269276
<td><code>false</code></td>
270277
<td> Accepts a React Native <code>ViewStyle</code> object. This applies to the container of each item in the grid layout and can be used to create a gap between each grid item with padding, apply background color, etc. </td>
271278
</tr>
279+
280+
<tr>
281+
<td><code>onHorizontalEndReached</code></td>
282+
<td><code>() => void</code></td>
283+
<td><code>undefined</code></td>
284+
<td><code>false</code></td>
285+
<td>Defines a callback function that is called when the horizontal scroll position reaches the <code>onHorizontalEndReachedThreshold</code>. Useful for loading more data as the user scrolls horizontally.</td>
286+
</tr>
287+
<tr>
288+
<td><code>onHorizontalEndReachedThreshold</code></td>
289+
<td><code>number</code></td>
290+
<td><code>0.5</code></td>
291+
<td><code>false</code></td>
292+
<td>Defines the threshold for triggering <code>onHorizontalEndReached</code>. Represented as a fraction of the total width of the scrollable grid, indicating how far from the end the horizontal scroll must be to trigger the event.</td>
293+
</tr>
294+
<tr>
295+
<td><code>onVerticalEndReached</code></td>
296+
<td><code>() => void</code></td>
297+
<td><code>undefined</code></td>
298+
<td><code>false</code></td>
299+
<td>Defines a callback function that is called when the vertical scroll position reaches the <code>onVerticalEndReachedThreshold</code>. Useful for loading more data as the user scrolls vertically.</td>
300+
</tr>
301+
<tr>
302+
<td><code>onVerticalEndReachedThreshold</code></td>
303+
<td><code>number</code></td>
304+
<td><code>0.5</code></td>
305+
<td><code>false</code></td>
306+
<td>Defines the threshold for triggering <code>onVerticalEndReached</code>. Represented as a fraction of the total height of the scrollable grid, indicating how far from the end the vertical scroll must be to trigger the event.</td>
307+
</tr>
308+
272309
</tbody>
273310
</table>
274311

@@ -300,8 +337,15 @@ A lower value results in more frequent updates, offering smoother visual updates
300337
<td><code>Function</code></td>
301338
<td><code> () => null</code></td>
302339
<td><code>true</code></td>
303-
<td>Its used for defining how each individual item should appear, it allows utilizing the item's properties to construct a custom layout or UI for that item within the grid.</td>
340+
<td>Defines how each individual item should appear, it allows utilizing the item's properties to construct a custom layout or UI for that item within the grid.</td>
304341
</tr>
342+
<tr>
343+
<td><code>keyExtractor</code></td>
344+
<td><code>(item, index) => string</code></td>
345+
<td><code>Function</code></td>
346+
<td><code>false</code></td>
347+
<td> Defines a function that extracts a unique key for a given item in the list. By default, the <code>keyExtractor</code> uses the item's index in the array as the key. This is crucial for optimizing the rendering and re-rendering of list items by providing a stable identity. The function receives an item from the data array and its index, and should return a unique string. Providing a custom <code>keyExtractor</code> is recommended when the items have a unique identifier other than the index, especially in cases where the list's order might change, or items are dynamically added or removed, to ensure consistent and efficient updates. </td>
348+
</tr>
305349
<tr>
306350
<td><code>maxItemsPerColumn</code></td>
307351
<td><code>Number</code></td>
@@ -314,7 +358,7 @@ A lower value results in more frequent updates, offering smoother visual updates
314358
<td><code>Number</code></td>
315359
<td><code>containerWidth / maxItemsPerColumn</code></td>
316360
<td><code>false</code></td>
317-
<td> Specifies the base unit height for items within the grid. This value serves as a foundational measure to determine the actual height of each grid item. The item's final height is calculated by multiplying this base unit height (<code>itemUnitHeight</code>) by the item's heightRatio, allowing for proportional scaling of items based on their content or design requirements. While <code>widthRatio</code> affects the item's width in relation to the column width, <code>itemUnitHeight</code> and <code>heightRatio</code> together define the item's vertical dimension, enabling dynamic grid layouts that adapt seamlessly to varying content sizes.</td>
361+
<td> Defines the base unit height for items within the grid. This value serves as a foundational measure to determine the actual height of each grid item. The item's final height is calculated by multiplying this base unit height (<code>itemUnitHeight</code>) by the item's heightRatio, allowing for proportional scaling of items based on their content or design requirements. While <code>widthRatio</code> affects the item's width in relation to the column width, <code>itemUnitHeight</code> and <code>heightRatio</code> together define the item's vertical dimension, enabling dynamic grid layouts that adapt seamlessly to varying content sizes.</td>
318362
</tr>
319363
<tr>
320364
<td><code>virtualization</code></td>
@@ -346,7 +390,7 @@ While, a lower buffer factor reduces the off-screen component count, optimizing
346390
<td><code>Number</code></td>
347391
<td><code>200</code></td>
348392
<td><code>false</code></td>
349-
<td>Specifies the interval, in milliseconds, at which scroll events are processed for the purpose of recalculating visible items and buffer in a virtualized grid. By setting this prop, developers can throttle the frequency of scroll event handling, optimizing performance during rapid scrolling by reducing the computational load associated with updating the list of visible items and buffer.
393+
<td>Defines the interval, in milliseconds, at which scroll events are processed for the purpose of recalculating visible items and buffer in a virtualized grid. By setting this prop, developers can throttle the frequency of scroll event handling, optimizing performance during rapid scrolling by reducing the computational load associated with updating the list of visible items and buffer.
350394

351395
A lower value results in more frequent updates, offering smoother visual updates of the grid's content at the potential cost of higher computational overhead. While, a higher interval decreases the frequency of updates, potentially improving performance but with less immediate recalculation triggered by scroll actions. This is crucial for fine-tuning the performance and experience of virtualized grid.
352396
</td>
@@ -372,6 +416,21 @@ A lower value results in more frequent updates, offering smoother visual updates
372416
<td><code>false</code></td>
373417
<td> Accepts a React Native <code>ViewStyle</code> object. This applies to the container of each item in the grid layout and can be used to create a gap between each grid item with padding, apply background color, etc. </td>
374418
</tr>
419+
<tr>
420+
<td><code>onEndReached</code></td>
421+
<td><code>() => void</code></td>
422+
<td><code>undefined</code></td>
423+
<td><code>false</code></td>
424+
<td> Defines a callback function that is triggered when the scroll reaches near the end of the scrollable grid. Useful for loading more data as the user scrolls horizontally </td>
425+
</tr>
426+
<tr>
427+
<td><code>onEndReachedThreshold</code></td>
428+
<td><code>number</code></td>
429+
<td><code>0.5</code></td>
430+
<td><code>false</code></td>
431+
<td> Defines the distance from the end of the content at which <code>onEndReached</code> should be triggered, expressed as a proportion of the total content length. For example, a value of <code>0.1</code> triggers the callback when the user has scrolled to within 10% of the end of the content. </td>
432+
</tr>
433+
375434
</tbody>
376435
</table>
377436

example/src/App.tsx

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,13 @@ import { SafeAreaView } from 'react-native';
1111
import InstagramExploreExample from './InstagramExploreExample';
1212
import PinterestExample from './PinterestHomeExample';
1313
import GridBoardExamplePage from './GridBoardExample';
14-
import TileGrid from './TileGrid';
14+
import InfiniteScrollExample from './InfiniteScrollExample';
1515

1616
type ScreenName =
1717
| 'Landing'
1818
| 'InstagramExplore'
1919
| 'Pinterest'
20-
| 'TileGrid'
20+
| 'InfiniteScrollExample'
2121
| 'GridBoardExample';
2222

2323
interface LandingProps {
@@ -27,13 +27,6 @@ interface LandingProps {
2727
const Landing = ({ onNavigate }: LandingProps) => {
2828
return (
2929
<View style={styles.container}>
30-
<TouchableOpacity
31-
onPress={() => onNavigate('TileGrid')}
32-
style={styles.button}
33-
>
34-
<Text style={styles.text}>TileGrid Example </Text>
35-
</TouchableOpacity>
36-
3730
<TouchableOpacity
3831
onPress={() => onNavigate('InstagramExplore')}
3932
style={styles.button}
@@ -54,6 +47,13 @@ const Landing = ({ onNavigate }: LandingProps) => {
5447
>
5548
<Text style={styles.text}>Grid Board Example </Text>
5649
</TouchableOpacity>
50+
51+
<TouchableOpacity
52+
onPress={() => onNavigate('InfiniteScrollExample')}
53+
style={styles.button}
54+
>
55+
<Text style={styles.text}>Infinite Scroll Example </Text>
56+
</TouchableOpacity>
5757
</View>
5858
);
5959
};
@@ -86,7 +86,7 @@ const App = () => {
8686
return (
8787
<SafeAreaView style={styles.safeAreaContainer}>
8888
{currentScreen === 'Landing' && <Landing onNavigate={navigate} />}
89-
{currentScreen === 'TileGrid' && <TileGrid />}
89+
{currentScreen === 'InfiniteScrollExample' && <InfiniteScrollExample />}
9090
{currentScreen === 'InstagramExplore' && <InstagramExploreExample />}
9191
{currentScreen === 'Pinterest' && <PinterestExample />}
9292
{currentScreen === 'GridBoardExample' && <GridBoardExamplePage />}

example/src/GridBoardExample.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import { StyleSheet, View, Text, Image, ScrollView } from 'react-native';
66
import { FlexGrid } from 'react-native-flexible-grid';
77

88
export default function GridBoardExamplePage() {
9+
let idCounter = React.useRef(0);
10+
911
//generate sample data
1012
const data = () => {
1113
const items = [];
@@ -21,6 +23,7 @@ export default function GridBoardExamplePage() {
2123
tweetImage: 'https://picsum.photos/200/300?random=' + i,
2224
widthRatio: 6,
2325
heightRatio: 6,
26+
id: ++idCounter.current,
2427
});
2528
} else {
2629
items.push({
@@ -31,14 +34,15 @@ export default function GridBoardExamplePage() {
3134
'The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog',
3235
widthRatio: 6,
3336
heightRatio: 3,
37+
id: ++idCounter.current,
3438
});
3539
}
3640
}
3741

3842
return items;
3943
};
4044

41-
const renderItem = (item: any, _: number) => {
45+
const renderItem = ({ item }: any) => {
4246
return (
4347
<View
4448
style={{
@@ -167,6 +171,7 @@ export default function GridBoardExamplePage() {
167171
itemContainerStyle={{
168172
padding: 10,
169173
}}
174+
keyExtractor={(item) => item.id.toString()}
170175
/>
171176

172177
<Text

example/src/InfiniteScrollExample.tsx

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
/* eslint-disable react-hooks/exhaustive-deps */
2+
/* eslint-disable react-native/no-inline-styles */
3+
4+
import React, { useState, useEffect } from 'react';
5+
import { View, Text, TouchableOpacity } from 'react-native';
6+
import { ResponsiveGrid } from 'react-native-flexible-grid';
7+
8+
const InfiniteScrollExample = () => {
9+
const [data, setData] = useState<DataProp[]>([]);
10+
const [loading, setLoading] = useState(false);
11+
12+
let idCounter = React.useRef(0);
13+
14+
interface DataProp {
15+
widthRatio: number;
16+
heightRatio: number;
17+
id: number;
18+
[key: string]: any;
19+
}
20+
21+
const getData = (repeatedTimes: number) => {
22+
const originalData = [
23+
{ widthRatio: 1, heightRatio: 1 },
24+
{ widthRatio: 1, heightRatio: 1 },
25+
{ widthRatio: 1, heightRatio: 2 },
26+
{ widthRatio: 1, heightRatio: 3 },
27+
{ widthRatio: 2, heightRatio: 1 },
28+
{ widthRatio: 3, heightRatio: 1 },
29+
];
30+
31+
let clonedData: DataProp[] = [];
32+
33+
for (let i = 0; i < repeatedTimes; i++) {
34+
const newData = originalData.map((item) => ({
35+
...item,
36+
id: ++idCounter.current,
37+
}));
38+
clonedData = [...clonedData, ...newData];
39+
}
40+
41+
return clonedData;
42+
};
43+
44+
useEffect(() => {
45+
loadData();
46+
}, []);
47+
48+
const loadData = () => {
49+
console.log('loadData called');
50+
if (loading) return; // Prevent multiple loads
51+
setLoading(true);
52+
53+
setTimeout(() => {
54+
// Simulate network request
55+
const newData = getData(5);
56+
setData((prevData) => [...prevData, ...newData]); // Append new data
57+
setLoading(false);
58+
}, 1500);
59+
};
60+
61+
// const renderFooter = () => {
62+
// if (!loading) return null;
63+
// return (
64+
// <View style={styles.loader}>
65+
// <ActivityIndicator size="large" />
66+
// </View>
67+
// );
68+
// };
69+
70+
const renderItem = ({ item }: any) => {
71+
return (
72+
<TouchableOpacity
73+
style={{
74+
flex: 1,
75+
padding: 1,
76+
}}
77+
onPress={() => {
78+
console.log(item.id, 'pressed');
79+
}}
80+
>
81+
<View
82+
style={{
83+
width: '100%',
84+
height: '100%',
85+
backgroundColor: '#FFA502',
86+
justifyContent: 'center',
87+
alignItems: 'center',
88+
borderRadius: 5,
89+
}}
90+
>
91+
<Text
92+
style={{
93+
color: 'white',
94+
fontSize: 20,
95+
}}
96+
>
97+
{item.id}
98+
</Text>
99+
</View>
100+
</TouchableOpacity>
101+
);
102+
};
103+
104+
return (
105+
<ResponsiveGrid
106+
maxItemsPerColumn={4}
107+
data={data}
108+
itemContainerStyle={{
109+
padding: 2,
110+
}}
111+
renderItem={renderItem}
112+
style={{
113+
backgroundColor: 'white',
114+
}}
115+
//FooterComponent={renderFooter}
116+
keyExtractor={(item) => item.id}
117+
onEndReached={loadData}
118+
onEndReachedThreshold={0.5}
119+
virtualization={false}
120+
/>
121+
);
122+
};
123+
124+
export default InfiniteScrollExample;

0 commit comments

Comments
 (0)