الفرق بين المراجعتين ل"ReactNative/flatlist"
جميل-بيلوني (نقاش | مساهمات) ط (←المعاملات:) |
جميل-بيلوني (نقاش | مساهمات) ط |
||
(مراجعة متوسطة واحدة بواسطة نفس المستخدم غير معروضة) | |||
سطر 710: | سطر 710: | ||
* [https://facebook.github.io/react-native/docs/flatlist صفحة FlatList في توثيق React Native الرسمي.] | * [https://facebook.github.io/react-native/docs/flatlist صفحة FlatList في توثيق React Native الرسمي.] | ||
[[تصنيف:ReactNative]] | [[تصنيف:ReactNative]] | ||
+ | [[تصنيف:React Native Component]] |
المراجعة الحالية بتاريخ 14:01، 9 أكتوبر 2021
واجهة عالية الأداء لتصيير قوائم مسطحة بسيطة تدعم الميزات الأكثر فائدة:
- عابر منصات (أو متعدّد المنصات: cross-platform) بالكامل.
- وضع أفقي اختياري.
- دوال رد نداء قابلة للضبط للتحكم بقابلية العرض (viewability callbacks).
- دعم الترويسة (Header).
- دعم التذييل (Footer).
- دعم الفواصل.
- التحديث بالسحب.
- التحميل أثناء التمرير (Scroll loading).
- دعم التمرير إلى فهرس باستخدام
ScrollToIndex
. - دعم الأعمدة المتعددة.
إذا احتجت لاستعمال الأقسام، فاستخدم <SectionList>
.
إليك مقال بسيط (تجربة حية):
import React from 'react';
import { SafeAreaView, View, FlatList, StyleSheet, Text, StatusBar } from 'react-native';
const DATA = [
{
id: 'bd7acbea-c1b1-46c2-aed5-3ad53abb28ba',
title: 'First Item',
},
{
id: '3ac68afc-c605-48d3-a4f8-fbd91aa97f63',
title: 'Second Item',
},
{
id: '58694a0f-3da1-471f-bd96-145571e29d72',
title: 'Third Item',
},
];
const Item = ({ title }) => (
<View style={styles.item}>
<Text style={styles.title}>{title}</Text>
</View>
);
const App = () => {
const renderItem = ({ item }) => (
<Item title={item.title} />
);
return (
<SafeAreaView style={styles.container}>
<FlatList
data={DATA}
renderItem={renderItem}
keyExtractor={item => item.id}
/>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: StatusBar.currentHeight || 0,
},
item: {
backgroundColor: '#f9c2ff',
padding: 20,
marginVertical: 8,
marginHorizontal: 16,
},
title: {
fontSize: 32,
},
});
export default App;
لتصيير أعمدة متعددة، استخدم خاصية numColumns
يمكن أن يؤدي استخدام هذا الأسلوب بدلاً من تخطيط flexWrap
إلى منع التعارضات مع منطق ارتفاع العنصر (item height logic).
إليك مثال أعقد:
- بتمرير
extraData={selectedId}
إلىFlatList
، نتأكد من إعادة تصييرFlatList
نفسه عندما يحدث تغير في الحالة. إذا لم تُعيَّن هذه الخاصية فلن يعلم مكوّنFlatList
أنه يحتاج إلى إعادة تصيير أي عناصر لأنه أيضًا مكوِّنٌ من النوعPureComponent
ولن تعرض مقارنة الخاصيات أي تغييرات.
- يخبر
keyExtractor
القائمة باستخدام المعرفات (id
) لمفاتيح react بدلاً من خاصية المفتاحkey
الافتراضية.
شيفرة المثال (تجربة حية):
import React, { useState } from "react";
import { FlatList, SafeAreaView, StatusBar, StyleSheet, Text, TouchableOpacity } from "react-native";
const DATA = [
{
id: "bd7acbea-c1b1-46c2-aed5-3ad53abb28ba",
title: "First Item",
},
{
id: "3ac68afc-c605-48d3-a4f8-fbd91aa97f63",
title: "Second Item",
},
{
id: "58694a0f-3da1-471f-bd96-145571e29d72",
title: "Third Item",
},
];
const Item = ({ item, onPress, backgroundColor, textColor }) => (
<TouchableOpacity onPress={onPress} style={[styles.item, backgroundColor]}>
<Text style={[styles.title, textColor]}>{item.title}</Text>
</TouchableOpacity>
);
const App = () => {
const [selectedId, setSelectedId] = useState(null);
const renderItem = ({ item }) => {
const backgroundColor = item.id === selectedId ? "#6e3b6e" : "#f9c2ff";
const color = item.id === selectedId ? 'white' : 'black';
return (
<Item
item={item}
onPress={() => setSelectedId(item.id)}
backgroundColor={{ backgroundColor }}
textColor={{ color }}
/>
);
};
return (
<SafeAreaView style={styles.container}>
<FlatList
data={DATA}
renderItem={renderItem}
keyExtractor={(item) => item.id}
extraData={selectedId}
/>
</SafeAreaView>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: StatusBar.currentHeight || 0,
},
item: {
padding: 20,
marginVertical: 8,
marginHorizontal: 16,
},
title: {
fontSize: 32,
},
});
export default App;
هذا مغلِّف ملائم حول <VirtualizedList>
، وبالتالي فإنّه يرث خاصياته (بالإضافة إلى خاصيات <ScrollView>
) التي لم تُصنَّف بشكل صريح هنا، إضافةً إلى التحذيرات التالية:
- لا يُحتفظ بالحالة الداخلية عند تمرير (scroll) المحتوى خارج نافذة التصيير. تأكد من تسجيل جميع بياناتك في بيانات العنصر أو المخازن الخارجية مثل Flux أو Redux أو Relay.
- هذا مكوّنُ
PureComponent
، ما يعني أنه لن يعاد تصييره إذا ظلت الخاصيات props
متساويّةً سَطحيًّا (shallow-equal). تأكّد من تمرير كل ما تعتمد عليه دالةrenderItem
كخاصياتٍ (مثلextraData
) لا تكون متساويًّةً (===
) بعد التحديثات، وإلا فقد لا تُحدَّث واجهة مستخدمك عند حدوث التغييرات. يتضمن هذا خاصيةdata
وحالة المكون الأب.
- لتقييد الذاكرة وتمكين التمرير السلس (smooth scrolling)، يُصيَّر المحتوى على نحو غير متزامن في الخلفيّة. هذا يعني أنه من الممكن التمرير أسرع من معدل التعبئة ورؤية المحتوى الفارغ مؤقتا. هذه مقايضة يمكن تعديلها لتناسب احتياجات كل تطبيق، وسيعمل فريق إطار العمل على تحسين هذا الأمر.
- افتراضيا، تبحث القائمة عن خاصية
key
في كل عنصر وتستخدم قيمتها كمفتاح React (أو React key). كبديل، يمكنك توفير خاصيةkeyExtractor
مخصصة.
الخاصيات
يرث هذا المكوّن خاصياتِ المكوّن ScrollView
أيضًا، إلا إذا كان متداخلًا في قائمة FlatList
أخرى بنفس الاتجاه.
renderItem
تأخذ عنصرًا من الخاصيّة data
وتُصيِّره في القائمة.
تُوفر بيانات وصفية إضافية مثل index
إذا احتجتها، بالإضافة إلى دالة separators.updateProps
أكثر عمومية، والتي تتيح لك تعيين الخاصيات التي تريد تغيير تصييرها إما بفاصل سابق (leading separator) أو بفاصل لاحِق (trailing separator)، وهذا في حالة كانت highlight
أو unhighlight
الأكثر شيوعًا (والتي تعيّن خاصية highlighted: boolean
) غيرَ كافيةٍ بالنسبة لك.
النوع | مطلوب |
---|---|
دالة | نعم |
item
(كائن): العنصر قيد التصيير من عناصرdata
.index
(عدد): الفهرس المقابل لهذا العنصر في مصفوفةdata
.separators
(كائن)highlight
(دالة)unhighlight
(دالة)updateProps
(دالة)select
: ثابت متعدّدenum('leading', 'trailing')
newProps
(كائن)
مثال:
<FlatList
ItemSeparatorComponent={
Platform.OS !== 'android' &&
(({ highlighted }) => (
<View
style={[
style.separator,
highlighted && { marginLeft: 0 }
]}
/>
))
}
data={[{ title: 'Title Text', key: 'item1' }]}
renderItem={({ item, index, separators }) => (
<TouchableHighlight
key={item.key}
onPress={() => this._onPress(item)}
onShowUnderlay={separators.highlight}
onHideUnderlay={separators.unhighlight}>
<View style={{ backgroundColor: 'white' }}>
<Text>{item.title}</Text>
</View>
</TouchableHighlight>
)}
/>
data
لتبسيط الأمور، الخاصيّة data
مجرد مصفوفة عادية. إذا أردت استخدام شيء آخر، مثل قائمة غير قابلة للتغيير (immutable list)، فاستخدم مكوّن VirtualizedList
الأساسيّ مباشرةً.
النوع | مطلوب |
---|---|
مصفوفة | نعم |
ItemSeparatorComponent
يُصيَّر بين كل عنصر، ولكن ليس في الجزء العلوي أو السفلي. افتراضيًا، تُوفَّر خاصيات highlighted
و leadingItem
. يوفر renderItem
كلا من separators.highlight
و separators.unhighlight
والذي سيحدِّث خاصية highlighted
، ولكن يمكنك أيضًا إضافة خاصيات مخصصةٍ باستخدام separators.updateProps
.
النوع | مطلوب |
---|---|
مكوّن | لا |
ListEmptyComponent
يُصيَّر عندما تكون القائمة فارغة. يمكن أن يكون مكون React (مثل SomeComponent
) أو عنصر React (مثل <SomeComponent />
).
النوع | مطلوب |
---|---|
مكون، عنصر | لا |
يُصيّر أسفلَ جميع العناصر. يمكن أن يكون مكون React (مثل SomeComponent
) أو عنصر React (مثل <SomeComponent />
).
النوع | مطلوب |
---|---|
مكون، عنصر | لا |
نمط للواجهة View
الداخلية لمكّون ListFooterComponent
.
النوع | مطلوب |
---|---|
كائن نمط (style object) | لا |
ListHeaderComponent
يُصيّر أعلى جميع العناصر. يمكن أن يكون مكون React (مثل SomeComponent
) أو عنصر React (مثل <SomeComponent />
).
النوع | مطلوب |
---|---|
مكون، عنصر | لا |
ListHeaderComponentStyle
نمط العرض الداخلي للمكوّن ListHeaderComponent
.
النوع | مطلوب |
---|---|
كائن نمط (style objects) | لا |
columnWrapperStyle
نمط مخصص اختياري للصفوف متعددة العناصر المولّدة عند تحّقق الشرط numColumns > 1
.
النوع | مطلوب |
---|---|
كائن نمط | لا |
extraData
خاصية تعليم (marker property) لإخبار القائمة بإعادة التصيير (نظرًا لأنها تنفّذ PureComponent
). إذا كانت أي من دوالك renderItem
، و Header
، و Footer
، وما إلى ذلك، تعتمد على أي شيء خارج خاصية data
، فضعها هنا وعالجها بطريقة غير قابلة للتغيير (immutably).
النوع | مطلوب |
---|---|
أي نوع any | لا |
getItemLayout
(data, index) => {length: number, offset: number, index: number}
الخاصية getItemLayout
هي تحسين اختياري تتيح لنا تخطي قياس المحتوى الديناميكي إذا كنت تعرف أبعاد العناصر في وقت مبكر. تعَدّ getItemLayout
طريقة فعالة وسهلة الاستخدام إذا كان لديك عناصر ثابتة الحجم، على سبيل المثال:
getItemLayout={(data, index) => (
{length: ITEM_HEIGHT, offset: ITEM_HEIGHT * index, index}
)}
يمكن أن تكون إضافة getItemLayout
بمثابة تعزيز أداء رائع لقوائم تضم مئات العناصر. تذكر تضمين طول الفاصل (الارتفاع أو العرض) في حساب الإزاحة إذا قمت بتحديد ItemSeparatorComponent
.
النوع | مطلوب |
---|---|
دالة | لا |
horizontal
في حالة القيمة true
، يصيِّر العناصر قرب بعضها البعض أفقيًا بدلاً من تكديسها رأسيًا.
النوع | مطلوب |
---|---|
قيمة منطقية | لا |
initialNumToRender
عدد العناصر المراد تصييرها في الدفعة الأولية. هذا يجب أن يكون كافيا لملء الشاشة لا أكثر. لاحظ أن هذه العناصر لن يُلغى تركيبها أبدًا كجزء من التصيير ذو النوافذ (windowed rendering) لتحسين الأداء المتوقع لإجراءات التمرير إلى الأعلى.
النوع | مطلوب |
---|---|
عدد | لا |
initialScrollIndex
بدلاً من البدء من الأعلى بالعنصر الأول، ابدأ من العنصر ذو الفهرَس الموافق للخاصيّة initialScrollIndex
. يؤدي هذا إلى تعطيل ميّزة "التمرير إلى أعلى" التي تُحافظ دائمًا على تصيير أول عناصر initialNumToRender
وتُصيّر فورًا العناصر بدءًا من هذا الفهرس الأولي، يتطلب تنفيذ getItemLayout
.
النوع | مطلوب |
---|---|
عدد | لا |
inverted
يعكس اتجاه التمرير. يستخدم تحويلات المقدار -1
.
النوع | مطلوب |
---|---|
قيمة منطقية | لا |
keyExtractor
(item: object, index: number) => string;
يُستخدم لاستخراج مفتاح فريد لعنصر معين في الفهرس المُحدَّد. يُستخدَم المفتاح للتخزين المؤقت وكمفتاح React لتتبع إعادة ترتيب العناصر. يتحقق المستخرج الافتراضي من قيمة item.key
، ثم item.id
ثم يعود إلى استخدام الفهرس كما يفعل React.
النوع | مطلوب |
---|---|
دالة | لا |
numColumns
يمكن تصيير أعمدة متعددة فقط باستعمال horizontal={false}
وسيتعرّج مثل تخطيط flexWrap
. يجب أن تكون جميع العناصر بنفس الارتفاع - تخطيطات البناء (masonry layouts) غير مدعومة.
النوع | مطلوب |
---|---|
عدد | لا |
onEndReached
(info: {distanceFromEnd: number}) => void
يُستدعى مرة واحدة عندما يكون موضع التمرير ضمن مجال onEndReachedThreshold
للمحتوى المصيّر.
النوع | مطلوب |
---|---|
دالة | لا |
onEndReachedThreshold
كم يجب أن تكون الحافة السفلية للقائمة من نهاية المحتوى (بوحدات الطول المرئي للقائمة) لتشغيل دالة رد النداء onEndReached
. وبالتالي، ستُطلِق القيمة 0.5 دالة رد النداء onEndReached
عندما تكون نهاية المحتوى ضمن نصف الطول المرئي للقائمة.
النوع | مطلوب |
---|---|
عدد | لا |
onRefresh
() => void
إذا توفّر، سيُضاف مكوّن RefreshControl
قياسيّ لوظيفة "السحب للتحديث (Pull to Refresh)". تأكد من ضبط خاصية refreshing
بشكل صحيح كذلك.
النوع | مطلوب |
---|---|
دالة | لا |
onViewableItemsChanged
يُستدعَى عندما تتغير قابلية عرض (viewability) الصفوف، كما هو مُحدَّد من طرف الخاصية viewabilityConfig
.
النوع | مطلوب |
---|---|
دالة(callback: { changed: array of ViewTokens, viewableItems: array of ViewTokens }) => void
|
لا |
progressViewOffset
عيِّن هذه الخاصيّة عند الحاجة إلى الإزاحة ليظهر مؤشر التحميل بشكل صحيح.
النوع | مطلوب |
---|---|
عدد | لا |
refreshing
عيّن لها القيمة true
أثناء انتظار بيانات جديدة من تحديث ما.
النوع | مطلوب |
---|---|
قيمة منطقية | لا |
removeClippedSubviews
قد يؤدي هذا إلى تحسين أداء التمرير للقوائم الكبيرة. القيمة الافتراضية على منصة Android هي true
.
ملاحظة: قد تكون هناك أخطاء (محتوى مفقود) في بعض الحالات. استخدمها على مسؤوليتك.
النوع | مطلوب |
---|---|
قيمة منطقية | لا |
viewabilityConfig
انظر ViewabilityHelper.js
لمعرفة نوع التدفق flow ولتفاصيل أكثر.
النوع | مطلوب |
---|---|
ViewabilityConfig | لا |
تأخذ الخاصية viewabilityConfig
نوع ViewabilityConfig
، وهو كائن ذو الخاصيات التالية:
الخاصية | النوع |
---|---|
minimumViewTime
|
عدد |
viewAreaCoveragePercentThreshold
|
عدد |
itemVisiblePercentThreshold
|
عدد |
waitForInteraction
|
قيمة منطقية |
أحد هذين على الأقل مطلوب: viewAreaCoveragePercentThreshold
أو itemVisiblePercentThreshold
. يجب القيام بهذا في دالة البناء constructor لتجنب الخطأ التالي (المرجع):
Error: Changing viewabilityConfig on the fly is not supported
constructor (props) {
super(props)
this.viewabilityConfig = {
waitForInteraction: true,
viewAreaCoveragePercentThreshold: 95
}
}
<FlatList
viewabilityConfig={this.viewabilityConfig}
...
minimumViewTime
الحد الأدنى من الوقت (بالمللي ثانية) الذي يجب أن يكون العنصر فيه قابلا للعرض فعليًا قبل إطلاق دالة رد نداء قابلية العرض (viewability callback). العدد الكبير يعني أن التمرير خلال المحتوى دون توقف لن يؤدي إلى تمييز المحتوى على أنه قابل للعرض.
viewAreaCoveragePercentThreshold
نسبة إطار العرض (viewport) الواجب تغطيتها لعنصر مغلق جزئيًا (partially occluded) ليُعدَّ على أنه "قابل للعرض (viewable)"، من 0 إلى 100. تُعدّ العناصر المرئية بالكامل قابلة للعرض دائمًا. تعني القيمة 0 أن بِكْسِل واحد في منفذ العرض يجعل العنصر قابلاً للعرض، والقيمة 100 تعني أن العنصر إما يجب أن يكون مرئيًا تمامًا أو يغطي منفذ العرض بالكامل ليُعدّ قابلًا للعرض.
itemVisiblePercentThreshold
تشبه viewAreaCoveragePercentThreshold
، ولكنها تأخذ في الاعتبار نسبة العنصر المرئي، بدلًا من جزءٍ من المساحة القابلة للعرض التي يغطّيها.
waitForInteraction
لا يمكن اعتبار أي شيء قابلاً للعرض حتى يمرِّر المستخدم أو عند استدعاء recordInteraction
بعد التصيير.
viewabilityConfigCallbackPairs
قائمة أزواج ViewabilityConfig
/ onViewableItemsChanged
.
ستُستدعى الدالة onViewableItemsChanged
عند استيفاء شروط ViewabilityConfig
ذات العلاقة.
انظر ViewabilityHelper.js
لمعرفة نوع flow ولتوثيق أكثر.
النوع | مطلوب |
---|---|
مصفوفة من ViewabilityConfigCallbackPair
|
لا |
التوابع
flashScrollIndicators()
flashScrollIndicators();
يعرض مؤشرات التمرير مؤقتا.
getNativeScrollRef()
getNativeScrollRef();
يوفر مرجعًا إلى مكون التمرير الأساسي.
getScrollResponder()
getScrollResponder();
يوفر مقبضًا (handle) لمستجيب التمرير الأساسي.
getScrollableNode()
getScrollableNode();
يوفر مقبضًا لعقدة التمرير الأساسية.
recordInteraction()
recordInteraction();
يخبر القائمة بحدوث تفاعل ما، والذي يجب أن يشغّل حسابات قابلية العرض (viewability calculations)، على سبيل المثال، إذا كان waitForInteractions
ذا قيمةٍ صحيحة (true
) ولم يمرِّر المستخدم. يُستدعى هذا عادةً عن طريق النقر على العناصر أو عن طريق إجراءات التنقل.
scrollToEnd()
scrollToEnd([params]);
يُمرِّر إلى نهاية المحتوى. قد يعمل بشكل سيّئٍ دون الخاصية getItemLayout
.
المعاملات:
الاسم | النوع | مطلوب | الوصف |
---|---|---|---|
params
|
كائن | لا | أنظر أدناه |
مفاتيح params
الصالحة هي:
-
'animated'
(قيمة منطقيّة): ما إذا وجَب تحريك القائمة أثناء التمرير. true
افتراضيًا.
scrollToIndex()
scrollToIndex(params);
تُمرِّر إلى العنصر الموجود في الفهرس المُحدَّد بحيث يوضع في المنطقة القابلة للعرض، بحيث يضعه viewPosition
بالقيمة 0
في الأعلى، والقيمة 1
في الأسفل، والقيمة 0.5
في المنتصف.
ملاحظة: لا يمكن التمرير إلى مواقع خارج نافذة التصيير دون تحديد الخاصية getItemLayout
.
المعاملات:
الاسم | النوع | مطلوب | الوصف |
---|---|---|---|
params
|
كائن | نعم | أنظر أدناه |
مفاتيح params
الصالحة هي:
-
'animated'
(قيمة منطقيّة): ما إذا وجَب تحريك القائمة أثناء التمرير. true
افتراضيًا. 'index'
(عدد): الفهرس المرغوب التمرير إليه. مطلوب.'viewOffset'
(عدد): عدد ثابت من وحدات البكسل لإزاحة (offset) الموضع الهدف النهائي. مطلوب.'viewPosition'
(عدد): تضع القيمة 0 العنصر المحدَّد بالفهرس في الأعلى، 1 في الأسفل، و0.5 في المنتصف.
scrollToItem()
scrollToItem(params);
يُمرِّر القائمة إلى العنصر المحدد، ويتطلب مسح البيانات (data) خطّيًّا، استخدم scrollToIndex
بدلاً منه إن أمكن.
ملاحظة: لا يمكن التمرير إلى مواقع خارج نافذة التصيير دون تحديد الخاصية getItemLayout
.
المعاملات:
الاسم | النوع | مطلوب | الوصف |
---|---|---|---|
params
|
كائن | نعم | أنظر أدناه |
مفاتيح params
الصالحة هي:
-
'animated'
(قيمة منطقيّة): ما إذا وجَب تحريك القائمة أثناء التمرير. true
افتراضيًا. 'item'
(كائن): العنصر المرغوب التمرير إليه. مطلوب.'viewPosition'
(عدد).
scrollToOffset()
scrollToOffset(params);
مرّر إلى محتوى معين في القائمة المزاح بقيمة محددة بالبكسل (content pixel offset).
المعاملات:
الاسم | النوع | مطلوب | الوصف |
---|---|---|---|
params
|
كائن | نعم | أنظر أدناه |
مفاتيح params
الصالحة هي:
-
'offset'
(عدد): الإزاحة المرغوب التمرير إليها. في حالة كانت قيمةُhorizontal
القيمةَ true
، فالإزاحة هي القيمة x، وفي أي حالة أخرى، تكون الإزاحة هي القيمة y. مطلوب.
-
'animated'
(قيمة منطقيّة): ما إذا وجَب تحريك القائمة أثناء التمرير.