المكون FlatList في React Native
واجهة أداء لتصيير قوائم مسطحة بسيطة تدعم الميزات الأكثر فائدة:
- عابر منصات (أو متعدّد المنصات: cross-platform) بالكامل.
- وضع أفقي اختياري.
- دوال رد نداء قابلة للضبط للتحكم بقابلية العرض (viewability callbacks).
- دعم الترويسة (Header).
- دعم التذييل (Footer).
- دعم الفواصل.
- التحديث بالسحب.
- التحميل أثناء التمرير (Scroll loading).
- دعم التمرير إلى فهرس باستخدام
ScrollToIndex
. - دعم الأعمدة المتعددة.
إذا احتجت لاستعمال الأقسام، فاستخدم <SectionList>
.
مثال بسيط:
<FlatList
data={[{key: 'a'}, {key: 'b'}]}
renderItem={({item}) => <Text>{item.key}</Text>}
/>
لتصيير أعمدة متعددة، استخدم خاصية numColumns
يمكن أن يؤدي استخدام هذا الأسلوب بدلاً من تخطيط flexWrap
إلى منع التعارضات مع منطق ارتفاع العنصر (item height logic).
مثال أعقد ومتعدد التحديد (multi-select) يوضح استخدام PureComponent
لتحسين الأداء وتجنب الأخطاء.
- من خلال ربط معالج
onPressItem
، ستبقى الخاصيات متساويّةً (===
) وستمنعPureComponent
إعادة تصييرات مسرفة ما لم تتغير خاصياتid
أوselected
أوtitle
، ولو لم تملك المكونات المصيّرة فيMyListItem
مثل هذه التحسينات.
- بتمرير
extraData={this.state}
إلى FlatList، نتأكد من إعادة تصيير FlatList نفسه عندما تتغير قيمة state.selected
. إذا لم تُعيَّن هذه الخاصية فلن يعلم مكوّن FlatList أنه يحتاج إلى إعادة تصيير أي عناصر لأنه أيضًا مكوِّنٌ من النوعPureComponent
ولن تعرض مقارنة الخاصيات أي تغييرات.
- يخبر
keyExtractor
القائمة باستخدام المعرفات (id
) لمفاتيح react بدلاً من خاصية المفتاحkey
الافتراضية.
class MyListItem extends React.PureComponent {
_onPress = () => {
this.props.onPressItem(this.props.id);
};
render() {
const textColor = this.props.selected ? 'red' : 'black';
return (
<TouchableOpacity onPress={this._onPress}>
<View>
<Text style={{color: textColor}}>{this.props.title}</Text>
</View>
</TouchableOpacity>
);
}
}
class MultiSelectList extends React.PureComponent {
state = {selected: (new Map(): Map<string, boolean>)};
_keyExtractor = (item, index) => item.id;
_onPressItem = (id: string) => {
// دوال التحديث مُفضَّلة للتحديثات التعامليّة
this.setState((state) => {
// انسخ الترابط
const selected = new Map(state.selected);
selected.set(id, !selected.get(id)); // toggle
return {selected};
});
};
_renderItem = ({item}) => (
<MyListItem
id={item.id}
onPressItem={this._onPressItem}
selected={!!this.state.selected.get(item.id)}
title={item.title}
/>
);
render() {
return (
<FlatList
data={this.props.data}
extraData={this.state}
keyExtractor={this._keyExtractor}
renderItem={this._renderItem}
/>
);
}
}
هذا مغلِّف ملائم حول <VirtualizedList>
، وبالتالي فإنّه يرث خاصياته (بالإضافة إلى خاصيات <ScrollView>
) التي لم تُصنَّف بشكل صريح هنا، إضافةً إلى التحذيرات التالية:
- لا يُحتفظ بالحالة الداخلية عند تمرير (scroll) المحتوى خارج نافذة التصيير. تأكد من تسجيل جميع بياناتك في بيانات العنصر أو المخازن الخارجية مثل Flux أو Redux أو Relay.
- هذا مكوّنُ
PureComponent
، ما يعني أنه لن يعاد تصييره إذا ظلت الخاصيات props
متساويّةً سَطحيًّا (shallow-equal). تأكّد من تمرير كل ما تعتمد عليه دالةrenderItem
كخاصياتٍ (مثلextraData
) لا تكون متساويًّةً (===
) بعد التحديثات، وإلا فقد لا تُحدَّث واجهة مستخدمك عند حدوث التغييرات. يتضمن هذا خاصيةdata
وحالة المكون الأب.
- لتقييد الذاكرة وتمكين التمرير السلس (smooth scrolling)، يُصيَّر المحتوى على نحو غير متزامن في الخلفيّة. هذا يعني أنه من الممكن التمرير أسرع من معدل التعبئة ورؤية المحتوى الفارغ مؤقتا. هذه مقايضة يمكن تعديلها لتناسب احتياجات كل تطبيق، وسيعمل فريق إطار العمل على تحسين هذا الأمر.
- افتراضيا، تبحث القائمة عن خاصية
key
في كل عنصر وتستخدم قيمتها كمفتاح React (أو React key). كبديل، يمكنك توفير خاصيةkeyExtractor
مخصصة.
يرث هذا المكوّن خاصياتِ المكوّن ScrollView
أيضًا، إلا إذا كان متداخلًا في قائمة FlatList أخرى بنفس الاتجاه.
flashScrollIndicators
getScrollResponder
getScrollableNode
scrollToEnd
scrollToIndex
scrollToItem
scrollToOffset
recordInteraction
الخاصيات
columnWrapperStyle
data
extraData
getItemLayout
horizontal
initialNumToRender
initialScrollIndex
inverted
ItemSeparatorComponent
keyExtractor
legacyImplementation
ListEmptyComponent
ListFooterComponent
ListFooterComponentStyle
ListHeaderComponent
ListHeaderComponentStyle
numColumns
onEndReached
onEndReachedThreshold
onRefresh
onViewableItemsChanged
progressViewOffset
refreshing
renderItem
removeClippedSubviews
viewabilityConfig
viewabilityConfigCallbackPairs
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
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، دالةَ تصييرٍ (render
)، أو عنصرًا مصيّرًا.
النوع | مطلوب |
---|---|
مكون، دالة، عنصر | لا |
يُصيّر أسفلَ جميع العناصر. يمكن أن يكون صنفَ مكونِ React، دالةَ تصييرٍ (render
)، أو عنصرًا مصيّرًا.
النوع | مطلوب |
---|---|
مكون، دالة، عنصر | لا |
نمط للعرض الداخلي لمكّون ListFooterComponent
.
النوع | مطلوب |
---|---|
كائن نمط (style object) | لا |
ListHeaderComponent
يُصيّر أعلى جميع العناصر. يمكن أن يكون صنفَ مكونِ React، دالةَ تصييرٍ (render
)، أو عنصرًا مصيّرًا.
النوع | مطلوب |
---|---|
مكون، دالة، عنصر | لا |
ListHeaderComponentStyle
نمط العرض الداخلي للمكوّن ListHeaderComponent
.
النوع | مطلوب |
---|---|
كائن نمط (style objects) | لا |
columnWrapperStyle
نمط مخصص اختياري للصفوف متعددة العناصر المولّدة عند تحّقق الشرط numColumns > 1
.
النوع | مطلوب |
---|---|
كائن نمط | لا |
extraData
خاصية تعليم (marker property) لإخبار القائمة بإعادة التصيير (نظرًا لأنها تنفّذ PureComponent
). إذا كانت أي من دوالك renderItem
، و Header
، و Footer
، وما إلى ذلك، تعتمد على أي شيء خارج خاصية data
، فضعها هنا وعالجها بطريقة غير قابلة للتغيير (immutably).
النوع | مطلوب |
---|---|
أي نوع كيفما كان | لا |
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
، ثم يعود إلى استخدام الفهرس كما يفعل 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
(info: {
viewableItems: array,
changed: array,
}) => void
يُستدعَى عندما تتغير قابلية عرض (viewability) الصفوف، كما هو مُحدَّد من طرف الخاصية viewabilityConfig
.
النوع | مطلوب |
---|---|
دالة | لا |
progressViewOffset
عيِّن هذه الخاصيّة عند الحاجة إلى الإزاحة ليظهر مؤشر التحميل بشكل صحيح.
النوع | مطلوب | المنصة |
---|---|---|
عدد | لا | Android |
legacyImplementation
طريقة عمل المكون القديمة، قد لا تكون لها ميزة تكافؤ كاملة وهي للتنقيح ومقارنة الأداء.
النوع | مطلوب |
---|---|
قيمة منطقية | لا |
refreshing
عيّن لها القيمة true
أثناء انتظار بيانات جديدة من تحديث ما.
النوع | مطلوب |
---|---|
قيمة منطقية | لا |
removeClippedSubviews
قد يؤدي هذا إلى تحسين أداء التمرير للقوائم الكبيرة.
ملاحظة: قد تكون هناك أخطاء (محتوى مفقود) في بعض الحالات. استخدمها على مسؤوليتك.
النوع | مطلوب |
---|---|
قيمة منطقية | لا |
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
|
لا |
التوابع
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'
(قيمة منطقيّة): ما إذا وجَب تحريك القائمة أثناء التمرير.
recordInteraction()
recordInteraction();
يخبر القائمة بحدوث تفاعل ما، والذي يجب أن يشغّل حسابات قابلية العرض (viewability calculations)، على سبيل المثال، إذا كان waitForInteractions
ذا قيمةٍ صحيحة (true
) ولم يمرِّر المستخدم. يُستدعى هذا عادةً عن طريق النقر على العناصر أو عن طريق إجراءات التنقل.
flashScrollIndicators()
flashScrollIndicators();
يعرض مؤشرات التمرير مؤقتا.
getScrollResponder()
getScrollResponder();
يوفر مقبضًا (handle) لمستجيب التمرير الأساسي.
GetScrollableNode()
getScrollableNode();
يوفر مقبضًا لعقدة التمرير الأساسية.