الفرق بين المراجعتين لصفحة: «ReactNative/direct manipulation»
لا ملخص تعديل |
جميل-بيلوني (نقاش | مساهمات) طلا ملخص تعديل |
||
(6 مراجعات متوسطة بواسطة 3 مستخدمين غير معروضة) | |||
سطر 1: | سطر 1: | ||
<noinclude>{{DISPLAYTITLE: المعالجة المباشرة في React Native}}</noinclude> | <noinclude>{{DISPLAYTITLE: المعالجة المباشرة في React Native}}</noinclude>من الضروري في بعض الأحيان إجراء تغييرات مباشرة على مكوّن دون استخدام الحالة أو الخاصيات لإطلاق إعادة تصييرٍ (re-render) لكامل الشجرة الفرعية (subtree). عند استخدام [[React]] في المتصفح على سبيل المثال، تحتاج أحيانًا إلى تعديل عقدة DOM مباشرةً، وينطبق الأمر نفسه على الواجهات في تطبيقات الجوال. تعد <code>setNativeProps</code> المكافئُ في React Native لضبط الخاصيات مباشرة على عقدة DOM.<blockquote>'''ملاحظة:''' استخدم setNativeProps عندما يؤدي إعادة التصيير المتكرر إلى إعاقةٍ في الأداء. | ||
لا يجب أن تستخدم المعالجة المباشرة كثيرًا. عادةً ما ستستخدمها فقط لإنشاء تحريكاتٍ مستمرة لتجنّب الحمل الزائد (overhead) في تصيير التسلسل الهرمي للمكونات (component hierarchy) والمطابقة (Reconciliation) بين العديد من الواجهات. تُعدّ الدالة <code>setNativeProps</code> دالّةً أمريّةً (imperative) وتُخزِّن الحالة في الطبقة الأصيلة (DOM، و UIView، إلخ) وليس في مكونات React الخاصة بك، مما يجعل شرح الشيفرة الخاصّة بك وفهمها أكثر صعوبة وتعقيدًا. قبل استخدامها، حاول حل مشكلتك باستخدام <code>[[React/react component#setState.28.29.E2.80.8E|setState]]</code> و <code>[[React/react component#shouldComponentUpdate.28.29.E2.80.8E|shouldComponentUpdate]]</code>.</blockquote> | |||
لا يجب أن تستخدم المعالجة المباشرة كثيرًا. عادةً ما ستستخدمها فقط لإنشاء تحريكاتٍ مستمرة لتجنّب الحمل الزائد (overhead) في تصيير التسلسل الهرمي للمكونات (component hierarchy) والمطابقة (Reconciliation) بين العديد من | |||
==setNativeProps مع مكون TouchableOpacity== | ==setNativeProps مع مكون TouchableOpacity== | ||
سطر 12: | سطر 7: | ||
<syntaxhighlight lang="javascript"> | <syntaxhighlight lang="javascript"> | ||
setOpacityTo(value) { | const viewRef = useRef(); | ||
const setOpacityTo = useCallback((value) => { | |||
// Redacted: animation related code | // Redacted: animation related code | ||
viewRef.current.setNativeProps({ | |||
opacity: value | opacity: value | ||
}); | }); | ||
}, | }, []); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
هذا يسمح لنا بكتابة الشيفرة البرمجية التالية ومعرفة أنّ عتامة المكون الابن سوف تُحدَّثُ استجابةً للّمس، دون أن يكون لدى المكون الابن أي معرفة بهذه الحقيقة أو يتطلب أي تغييرات في شيفرته: | هذا يسمح لنا بكتابة الشيفرة البرمجية التالية ومعرفة أنّ عتامة المكون الابن سوف تُحدَّثُ استجابةً للّمس، دون أن يكون لدى المكون الابن أي معرفة بهذه الحقيقة أو يتطلب أي تغييرات في شيفرته: | ||
<syntaxhighlight lang="javascript"> | <syntaxhighlight lang="javascript"> | ||
<TouchableOpacity onPress={ | <TouchableOpacity onPress={handlePress}> | ||
<View | <View> | ||
<Text>Press me!</Text> | <Text>Press me!</Text> | ||
</View> | </View> | ||
سطر 31: | سطر 27: | ||
لنتخّيّل أنّ ميّزة <code>setNativeProps</code> غير موجودة في إطار العمل. إحدى الطرق التي قد نستخدمها للتخلّص من هذا القيد هي تخزين قيمة العتامة في الحالة، ثم تحديث تلك القيمة في كلّ مرّة تُطلق فيها الدالة <code>onPress</code>: | لنتخّيّل أنّ ميّزة <code>setNativeProps</code> غير موجودة في إطار العمل. إحدى الطرق التي قد نستخدمها للتخلّص من هذا القيد هي تخزين قيمة العتامة في الحالة، ثم تحديث تلك القيمة في كلّ مرّة تُطلق فيها الدالة <code>onPress</code>: | ||
<syntaxhighlight lang="javascript"> | <syntaxhighlight lang="javascript"> | ||
const [buttonOpacity, setButtonOpacity] = useState(1); | |||
return ( | |||
<TouchableOpacity | |||
onPressIn={() => setButtonOpacity(0.5)} | |||
onPressOut={() => setButtonOpacity(1)}> | |||
<View style={{ opacity: buttonOpacity }}> | |||
<Text>Press me!</Text> | |||
</View> | |||
</TouchableOpacity> | |||
); | |||
</syntaxhighlight> | </syntaxhighlight> | ||
يتطلّب هذا حسابًا كثيفًا مقارنة بالمثال الأصلي، إذ يحتاج React إلى إعادة تصيير التسلسل المكونات الهرمي في كل مرة تتغير فيها العتامة، على الرغم من عدم تَغيُّر خاصيّات | يتطلّب هذا حسابًا كثيفًا مقارنة بالمثال الأصلي، إذ يحتاج React إلى إعادة تصيير التسلسل المكونات الهرمي في كل مرة تتغير فيها العتامة، على الرغم من عدم تَغيُّر خاصيّات الواجهة وخاصيات أبناء الواجهة الأخرى. هذا الحِمل ليس مصدرًا للقلق عادةً، لكن عند تنفيذ التحريكات المستمرة والاستجابة للإيماءات، فإن تحسين المكوّنات بحكمة يمكن أن يحسّن من دقة التحريكات. | ||
إذا نظرت إلى شيفرة <code>setNativeProps</code> في [https://github.com/facebook/react-native/blob/master/Libraries/Renderer/oss/ReactNativeRenderer-prod.js NativeMethodsMixin] ستلاحظ أنه غِلافٌ (wrapper) حول <code>RCTUIManager.updateView</code>. وهذا هو بالضبط نفس استدعاء الدالة الذي يُنتَج من إعادة التصيير. راجع <code>receiveComponent</code> في <code>ReactNativeBaseComponent.js</code>. | إذا نظرت إلى شيفرة <code>setNativeProps</code> في [https://github.com/facebook/react-native/blob/master/Libraries/Renderer/oss/ReactNativeRenderer-prod.js NativeMethodsMixin] ستلاحظ أنه غِلافٌ (wrapper) حول <code>RCTUIManager.updateView</code>. وهذا هو بالضبط نفس استدعاء الدالة الذي يُنتَج من إعادة التصيير. راجع <code>receiveComponent</code> في <code>[https://github.com/facebook/react-native/blob/fb2ec1ea47c53c2e7b873acb1cb46192ac74274e/Libraries/Renderer/oss/ReactNativeRenderer-prod.js#L5793-L5813 ReactNativeBaseComponent.js]</code>. | ||
==المكونات المركبة و <code>setNativeProps</code>== | ==المكونات المركبة و <code>setNativeProps</code>== | ||
المكونات المركبَّة غير مدعومة من طرف | المكونات المركبَّة غير مدعومة من طرف واجهة أصيلة، لذلك لا يمكنك استدعاء <code>setNativeProps</code> عليها. خذ هذا المثال ([https://snack.expo.dev/@hsoubwiki/setnativeprops-with-composite-components تجربة حية]): | ||
<syntaxhighlight lang="javascript"> | <syntaxhighlight lang="javascript"> | ||
import React from | import React from "react"; | ||
import { Text, TouchableOpacity, View } from | import { Text, TouchableOpacity, View } from "react-native"; | ||
const MyButton = (props) => ( | |||
<View style={{ marginTop: 50 }}> | |||
<Text>{props.label}</Text> | |||
</View> | |||
); | |||
export default | export default App = () => ( | ||
<TouchableOpacity> | |||
<MyButton label="Press me!" /> | |||
</TouchableOpacity> | |||
); | |||
</syntaxhighlight> | </syntaxhighlight> | ||
إذا قمت بتشغيل هذا سترى | إذا قمت بتشغيل هذا سترى الخطأ التالي فورًا:<syntaxhighlight lang="js"> | ||
Touchable child must either be native or forward setNativeProps to a native component | |||
</syntaxhighlight>يحدث هذا لأنّ المكوّن <code>MyButton</code> غير مدعوم مباشرةً من طرف واجهة أصيلة من المُفترَض أن تُعيَّن عتامته. يمكنك التفكير في الأمر كما يلي: إذا عرَّفتَ مكوِّنًا باستخدام <code>createReactClass</code> فلن تتوقع أن تكون قادرًا على تعيين خاصيّةِ style له لأنّها لن تعمَل، ستحتاج إلى تمرير الخاصيّة style إلى مُكوِّنٍ ابن، إلا إذا كنت تُغلِّفُ (wrapping) عنصرًا أصيلًا. وبالمثل، سنقوم بإعادة توجيه <code>setNativeProps</code> إلى مكونٍ ابنٍ مدعومٍ من واجهة أصيلة. | |||
===إعادة توجيه <code>setNativeProps</code> إلى مكون ابن=== | |||
بما أن التابع <code>setNativeProps</code> متوافر لكل مرجع لمكوّن من النوع <code>View</code> فيكفي إعادة توجيه مرجع لمكونك المخصص إلى أحد المكونات <code><View /></code> المصيرة. وهذا يعني أن استدعاء الدالّة <code>setNativeProps</code> على مكون مخصص له نفس تأثير استدعاء <code>setNativeProps</code> على المكون <code>View</code> المغلِّف نفسه. | |||
انظر المثال التالي ([https://snack.expo.dev/@hsoubwiki/forwarding-setnativeprops تجربة حية]): | |||
<syntaxhighlight lang="javascript"> | <syntaxhighlight lang="javascript"> | ||
import React from | import React from "react"; | ||
import { Text, TouchableOpacity, View } from | import { Text, TouchableOpacity, View } from "react-native"; | ||
const MyButton = React.forwardRef((props, ref) => ( | |||
<View {...props} ref={ref} style={{ marginTop: 50 }}> | |||
<Text>{props.label}</Text> | |||
</View> | |||
)); | |||
export default App = () => ( | |||
<TouchableOpacity> | |||
<MyButton label="Press me!" /> | |||
</TouchableOpacity> | |||
); | |||
export default | |||
</syntaxhighlight> | </syntaxhighlight> | ||
يمكنك الآن استخدام المكوّن <code>MyButton</code> داخل المكوّن <code>TouchableOpacity</code>! | يمكنك الآن استخدام المكوّن <code>MyButton</code> داخل المكوّن <code>TouchableOpacity</code>! | ||
قد تلاحظ أننا مررنا جميع الخاصيّات إلى | قد تلاحظ أننا مررنا جميع الخاصيّات إلى الواجهة الابن باستخدام <code>{...this.props}</code>. وسبب ذلك هو أن المكون <code>TouchableOpacity</code> هو في الواقع مكوّن مركب (composite component)، ولذا فبالإضافة إلى اعتماده على <code>setNativeProps</code> الموجودة في مكوّنه الابن، فإنه يتطلب أيضا أن يقوم المكون الابن بالتعامل مع اللمس. وللقيام بهذا فإنه يمرّر [[ReactNative/view#onMoveShouldSetResponder|عدّة خاصيّاتٍ]] تستدعي مجدّدًا مكوّن <code>TouchableOpacity</code>. وعلى النقيض من ذلك، فإن <code>TouchableHighlight</code> مدعوم من واجهة أصيلة ولا يتطلب سوى إنشاء <code>setNativeProps</code> في المكوّن. | ||
== استعمال <code>setNativeProps</code> لمسح قيمة حقل إدخال النص == | == استعمال <code>setNativeProps</code> لمسح قيمة حقل إدخال النص == | ||
مسح قيمة المكوّن | مسح قيمة المكوّن TextInput حالةٌ أخرى من حالات استخدام <code>setNativeProps</code> الشائعة. يمكن أحيانًا أن يؤدي استخدام الخاصيّة <code>controlled</code> التابعة للمكوّن <code>TextInput</code> إلى إسقاط المحارف عندما تكون الخاصيّة <code>bufferDelay</code> منخفضةً أو عندما يكتب المستخدم بسرعة كبيرة. يفضِّل بعض المطورين تخطي هذه الخاصيّة تمامًا واستخدامَ <code>setNativeProps</code> مباشرة بدلاً من ذلك لمعالجة قيمة المكوّن TextInput عند الضرورة. على سبيل المثال، توضح الشيفرة التالية كيفيّة مسح المدخلات عند النقر فوق زر ([https://snack.expo.dev/@hsoubwiki/clear-text-direct-manipulation تجربة حية]): | ||
<syntaxhighlight lang="javascript"> | <syntaxhighlight lang="javascript"> | ||
import React from | import React from "react"; | ||
import { TextInput, Text, TouchableOpacity, View } from | import { useCallback, useRef } from "react"; | ||
import { StyleSheet, TextInput, Text, TouchableOpacity, View } from "react-native"; | |||
const App = () => { | |||
clearText = () => { | const inputRef = useRef(); | ||
const clearText = useCallback(() => { | |||
} | inputRef.current.setNativeProps({ text: "" }); | ||
}, []); | |||
return ( | |||
<View style={styles.container}> | |||
<TextInput ref={inputRef} style={styles.input} /> | |||
<TouchableOpacity onPress={clearText}> | |||
<Text>Clear text</Text> | |||
</TouchableOpacity> | |||
</View> | |||
); | |||
}; | |||
const styles = StyleSheet.create({ | |||
container: { | |||
} | flex: 1, | ||
} | alignItems: "center", | ||
justifyContent: "center", | |||
}, | |||
input: { | |||
height: 50, | |||
width: 200, | |||
marginHorizontal: 20, | |||
borderWidth: 1, | |||
borderColor: "#ccc", | |||
}, | |||
}); | |||
export default App; | |||
</syntaxhighlight> | </syntaxhighlight> | ||
==تجنب النزاعات مع دالة التصيير== | ==تجنب النزاعات مع دالة التصيير== | ||
عند تحديث خاصيةٍ تُدارُ أيضًا بواسطة دالة التصيير، فقد ينتهي الأمر | عند تحديث خاصيةٍ تُدارُ أيضًا بواسطة دالة التصيير، فقد ينتهي الأمر إلى حدوث بعض العلل المربكة التي لا يمكن التنبؤ بها لأنه في كلّ مرّةٍ يُعاد فيها تصيير المكوّن وتتغيَّر هذه الخاصيّة، ستُتَجاهل أي قيمةٍ قد عُيِّنَت مسبقًا من <code>setNativeProps</code> بالكامل وستُتَجاوَز. | ||
==<code>setNativeProps</code> و<code>shouldComponentUpdate</code>== | ==<code>setNativeProps</code> و<code>shouldComponentUpdate</code>== | ||
سطر 152: | سطر 141: | ||
==توابع أصيلة أخرى== | ==توابع أصيلة أخرى== | ||
تتوفر التوابع الموضّحة هنا على معظم المكونات الافتراضية التي يقدمها React Native. ومع ذلك، لاحظ أنها غير متوفرة على المكونات المركّبة التي لا تُدعم مباشرةً من طرف | تتوفر التوابع الموضّحة هنا على معظم المكونات الافتراضية التي يقدمها React Native. ومع ذلك، لاحظ أنها غير متوفرة على المكونات المركّبة التي لا تُدعم مباشرةً من طرف واجهة أصيلة. سيتضمن هذا عمومًا معظم المكونات التي تُعرِّفها في تطبيقك الخاص. | ||
===<code>measure(callback)</code>=== | ===<code>measure(callback)</code>=== | ||
تُحدِّد الموقع على الشاشة والعرض والارتفاع | تُحدِّد الموقع على الشاشة والعرض والارتفاع للواجهة المحددة في الإطار المعروض، وتُعيد القيم عبر دالّة رد نداء غير متزامنة (async callback). في حالة نجاح ذلك، ستُستدعَى دالّة رد النّداء باستخدام المعاملات التالية: | ||
* | * x | ||
* | * y | ||
* | * width | ||
* | * height | ||
* | * pageX | ||
* | * pageY | ||
لاحظ أن هذه القياسات غير متوفّرة إلا بعد اكتمال التصيير على الجهة الأصيلة. إذا كنت بحاجة إلى القياسات في أقرب وقت | لاحظ أن هذه القياسات غير متوفّرة إلا بعد اكتمال التصيير على الجهة الأصيلة. إذا كنت بحاجة إلى القياسات في أقرب وقت ممكن ولا تحتاج <code>pageX</code> و <code>pageY</code> فانظر الخاصيّة [[ReactNative/view|<code>onLayout</code>]] كبديل. | ||
العرض والارتفاع اللذان يعيدهما <code>()measure</code> هما عرض وارتفاع المكون في الإطار المعروض، إذا كنت تريد حجم المكون الحقيقي فانظر استخدام الخاصية <code>[[ReactNative/view|onLayout]]</code>كبديل. | |||
===<code>measureInWindow(callback)</code>=== | ===<code>measureInWindow(callback)</code>=== | ||
تُحدِّد موقع | تُحدِّد موقع الواجهة المعطى في النافذة وتُعيد القيم عبر دالّة رد نداء غير متزامنة. إذا تم تضمين واجهة React الجذر (React root view) في واجهة أصيلٍ آخر، فسوف تُعطيك هذه الدالة الإحداثيات المطلقة (absolute coordinates). في حالة نجاح ذلك، ستُستدعَى دالّة رد النّداء باستخدام المعاملات التالية: | ||
* | * x | ||
* | * y | ||
* | * width | ||
* | * height | ||
===<code>measureLayout(relativeToNativeNode, onSuccess, onFail)</code>=== | ===<code>measureLayout(relativeToNativeNode, onSuccess, onFail)</code>=== | ||
مشابهة للدالة <code>measure()</code>، ولكنّها تقيس | مشابهة للدالة <code>measure()</code>، ولكنّها تقيس الواجهة نسبةً إلى مكوّنٍ جدٍّ (ancestor)، المحدَّد بالاسم <code>relativeToNativeNode</code>. هذا يعني أن القيمتان <code>x</code> و<code>y</code> نسبيّتان نسبةً إلى القيمتين الأصليتين <code>x</code> و<code>y</code> الخاصّتين بالواجهة الجد (ancestor view). | ||
'''ملاحظة''': يمكن استدعاء هذه الدالة مع معالج <code>relativeToNativeNode</code> أيضًا (بدلًا من المرجع)، لكن يهمل هذا الاختلاف. | |||
إليك المثال التالي ([https://snack.expo.dev/@hsoubwiki/measurelayout-example-direct-manipulation تجربة حية]): | |||
<syntaxhighlight lang="javascript"> | <syntaxhighlight lang="javascript"> | ||
import { | import React, { useEffect, useRef, useState } from "react"; | ||
import { Text, View, StyleSheet } from "react-native"; | |||
const App = () => { | |||
const textContainerRef = useRef(null); | |||
const textRef = useRef(null); | |||
const [measure, setMeasure] = useState(null); | |||
useEffect(() => { | |||
if (textRef.current && textContainerRef.current) { | |||
textRef.current.measureLayout( | |||
textContainerRef.current, | |||
(left, top, width, height) => { | |||
setMeasure({ left, top, width, height }); | |||
} | |||
); | |||
} | |||
}, [measure]); | |||
return ( | |||
<View style={styles.container}> | |||
<View | |||
ref={textContainerRef} | |||
style={styles.textContainer} | |||
> | |||
<Text ref={textRef}> | |||
Where am I? (relative to the text container) | |||
</Text> | |||
</View> | |||
<Text style={styles.measure}> | |||
{JSON.stringify(measure)} | |||
</Text> | |||
</View> | |||
); | |||
}; | |||
const styles = StyleSheet.create({ | |||
container: { | |||
flex: 1, | |||
justifyContent: "center", | |||
}, | |||
textContainer: { | |||
backgroundColor: "#61dafb", | |||
justifyContent: "center", | |||
alignItems: "center", | |||
padding: 12, | |||
}, | |||
measure: { | |||
textAlign: "center", | |||
padding: 12, | |||
}, | |||
}); | |||
export default App; | |||
</syntaxhighlight> | </syntaxhighlight> | ||
===<code>focus()</code>=== | ===<code>focus()</code>=== | ||
تطلب التركيز على المدخل أو | تطلب التركيز على المدخل أو الواجهة المعطاة. يعتمد السّلوك الذي سيُطلَق على نظام التشغيل ونوع الواجهة (view type). | ||
===<code>blur()</code>=== | ===<code>blur()</code>=== | ||
تُزيل التركيز على المدخل أو | تُزيل التركيز على المدخل أو الواجهة المعطى. وهي عكس <code>focus()</code>. | ||
== مصادر == | == مصادر == | ||
* [https://facebook.github.io/react-native/docs/direct-manipulation صفحة Direct Manipulation في توثيق React Native الرسمي.] | * [https://facebook.github.io/react-native/docs/direct-manipulation صفحة Direct Manipulation في توثيق React Native الرسمي.] | ||
[[تصنيف:ReactNative]] | [[تصنيف:ReactNative]] | ||
[[تصنيف:React Native Docs]] |
المراجعة الحالية بتاريخ 13:55، 9 أكتوبر 2021
من الضروري في بعض الأحيان إجراء تغييرات مباشرة على مكوّن دون استخدام الحالة أو الخاصيات لإطلاق إعادة تصييرٍ (re-render) لكامل الشجرة الفرعية (subtree). عند استخدام React في المتصفح على سبيل المثال، تحتاج أحيانًا إلى تعديل عقدة DOM مباشرةً، وينطبق الأمر نفسه على الواجهات في تطبيقات الجوال. تعد setNativeProps
المكافئُ في React Native لضبط الخاصيات مباشرة على عقدة DOM.
ملاحظة: استخدم setNativeProps عندما يؤدي إعادة التصيير المتكرر إلى إعاقةٍ في الأداء. لا يجب أن تستخدم المعالجة المباشرة كثيرًا. عادةً ما ستستخدمها فقط لإنشاء تحريكاتٍ مستمرة لتجنّب الحمل الزائد (overhead) في تصيير التسلسل الهرمي للمكونات (component hierarchy) والمطابقة (Reconciliation) بين العديد من الواجهات. تُعدّ الدالة
setNativeProps
دالّةً أمريّةً (imperative) وتُخزِّن الحالة في الطبقة الأصيلة (DOM، و UIView، إلخ) وليس في مكونات React الخاصة بك، مما يجعل شرح الشيفرة الخاصّة بك وفهمها أكثر صعوبة وتعقيدًا. قبل استخدامها، حاول حل مشكلتك باستخدامsetState
وshouldComponentUpdate
.
setNativeProps مع مكون TouchableOpacity
يستخدم مكوّن TouchableOpacity دالّة setNativeProps
داخليًا لتحديث عتامةِ (opacity) مكوّناته الأبناء:
const viewRef = useRef();
const setOpacityTo = useCallback((value) => {
// Redacted: animation related code
viewRef.current.setNativeProps({
opacity: value
});
}, []);
هذا يسمح لنا بكتابة الشيفرة البرمجية التالية ومعرفة أنّ عتامة المكون الابن سوف تُحدَّثُ استجابةً للّمس، دون أن يكون لدى المكون الابن أي معرفة بهذه الحقيقة أو يتطلب أي تغييرات في شيفرته:
<TouchableOpacity onPress={handlePress}>
<View>
<Text>Press me!</Text>
</View>
</TouchableOpacity>
لنتخّيّل أنّ ميّزة setNativeProps
غير موجودة في إطار العمل. إحدى الطرق التي قد نستخدمها للتخلّص من هذا القيد هي تخزين قيمة العتامة في الحالة، ثم تحديث تلك القيمة في كلّ مرّة تُطلق فيها الدالة onPress
:
const [buttonOpacity, setButtonOpacity] = useState(1);
return (
<TouchableOpacity
onPressIn={() => setButtonOpacity(0.5)}
onPressOut={() => setButtonOpacity(1)}>
<View style={{ opacity: buttonOpacity }}>
<Text>Press me!</Text>
</View>
</TouchableOpacity>
);
يتطلّب هذا حسابًا كثيفًا مقارنة بالمثال الأصلي، إذ يحتاج React إلى إعادة تصيير التسلسل المكونات الهرمي في كل مرة تتغير فيها العتامة، على الرغم من عدم تَغيُّر خاصيّات الواجهة وخاصيات أبناء الواجهة الأخرى. هذا الحِمل ليس مصدرًا للقلق عادةً، لكن عند تنفيذ التحريكات المستمرة والاستجابة للإيماءات، فإن تحسين المكوّنات بحكمة يمكن أن يحسّن من دقة التحريكات.
إذا نظرت إلى شيفرة setNativeProps
في NativeMethodsMixin ستلاحظ أنه غِلافٌ (wrapper) حول RCTUIManager.updateView
. وهذا هو بالضبط نفس استدعاء الدالة الذي يُنتَج من إعادة التصيير. راجع receiveComponent
في ReactNativeBaseComponent.js
.
المكونات المركبة و setNativeProps
المكونات المركبَّة غير مدعومة من طرف واجهة أصيلة، لذلك لا يمكنك استدعاء setNativeProps
عليها. خذ هذا المثال (تجربة حية):
import React from "react";
import { Text, TouchableOpacity, View } from "react-native";
const MyButton = (props) => (
<View style={{ marginTop: 50 }}>
<Text>{props.label}</Text>
</View>
);
export default App = () => (
<TouchableOpacity>
<MyButton label="Press me!" />
</TouchableOpacity>
);
إذا قمت بتشغيل هذا سترى الخطأ التالي فورًا:
Touchable child must either be native or forward setNativeProps to a native component
يحدث هذا لأنّ المكوّن MyButton
غير مدعوم مباشرةً من طرف واجهة أصيلة من المُفترَض أن تُعيَّن عتامته. يمكنك التفكير في الأمر كما يلي: إذا عرَّفتَ مكوِّنًا باستخدام createReactClass
فلن تتوقع أن تكون قادرًا على تعيين خاصيّةِ style له لأنّها لن تعمَل، ستحتاج إلى تمرير الخاصيّة style إلى مُكوِّنٍ ابن، إلا إذا كنت تُغلِّفُ (wrapping) عنصرًا أصيلًا. وبالمثل، سنقوم بإعادة توجيه setNativeProps
إلى مكونٍ ابنٍ مدعومٍ من واجهة أصيلة.
إعادة توجيه setNativeProps
إلى مكون ابن
بما أن التابع setNativeProps
متوافر لكل مرجع لمكوّن من النوع View
فيكفي إعادة توجيه مرجع لمكونك المخصص إلى أحد المكونات <View />
المصيرة. وهذا يعني أن استدعاء الدالّة setNativeProps
على مكون مخصص له نفس تأثير استدعاء setNativeProps
على المكون View
المغلِّف نفسه.
انظر المثال التالي (تجربة حية):
import React from "react";
import { Text, TouchableOpacity, View } from "react-native";
const MyButton = React.forwardRef((props, ref) => (
<View {...props} ref={ref} style={{ marginTop: 50 }}>
<Text>{props.label}</Text>
</View>
));
export default App = () => (
<TouchableOpacity>
<MyButton label="Press me!" />
</TouchableOpacity>
);
يمكنك الآن استخدام المكوّن MyButton
داخل المكوّن TouchableOpacity
!
قد تلاحظ أننا مررنا جميع الخاصيّات إلى الواجهة الابن باستخدام {...this.props}
. وسبب ذلك هو أن المكون TouchableOpacity
هو في الواقع مكوّن مركب (composite component)، ولذا فبالإضافة إلى اعتماده على setNativeProps
الموجودة في مكوّنه الابن، فإنه يتطلب أيضا أن يقوم المكون الابن بالتعامل مع اللمس. وللقيام بهذا فإنه يمرّر عدّة خاصيّاتٍ تستدعي مجدّدًا مكوّن TouchableOpacity
. وعلى النقيض من ذلك، فإن TouchableHighlight
مدعوم من واجهة أصيلة ولا يتطلب سوى إنشاء setNativeProps
في المكوّن.
استعمال setNativeProps
لمسح قيمة حقل إدخال النص
مسح قيمة المكوّن TextInput حالةٌ أخرى من حالات استخدام setNativeProps
الشائعة. يمكن أحيانًا أن يؤدي استخدام الخاصيّة controlled
التابعة للمكوّن TextInput
إلى إسقاط المحارف عندما تكون الخاصيّة bufferDelay
منخفضةً أو عندما يكتب المستخدم بسرعة كبيرة. يفضِّل بعض المطورين تخطي هذه الخاصيّة تمامًا واستخدامَ setNativeProps
مباشرة بدلاً من ذلك لمعالجة قيمة المكوّن TextInput عند الضرورة. على سبيل المثال، توضح الشيفرة التالية كيفيّة مسح المدخلات عند النقر فوق زر (تجربة حية):
import React from "react";
import { useCallback, useRef } from "react";
import { StyleSheet, TextInput, Text, TouchableOpacity, View } from "react-native";
const App = () => {
const inputRef = useRef();
const clearText = useCallback(() => {
inputRef.current.setNativeProps({ text: "" });
}, []);
return (
<View style={styles.container}>
<TextInput ref={inputRef} style={styles.input} />
<TouchableOpacity onPress={clearText}>
<Text>Clear text</Text>
</TouchableOpacity>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: "center",
justifyContent: "center",
},
input: {
height: 50,
width: 200,
marginHorizontal: 20,
borderWidth: 1,
borderColor: "#ccc",
},
});
export default App;
تجنب النزاعات مع دالة التصيير
عند تحديث خاصيةٍ تُدارُ أيضًا بواسطة دالة التصيير، فقد ينتهي الأمر إلى حدوث بعض العلل المربكة التي لا يمكن التنبؤ بها لأنه في كلّ مرّةٍ يُعاد فيها تصيير المكوّن وتتغيَّر هذه الخاصيّة، ستُتَجاهل أي قيمةٍ قد عُيِّنَت مسبقًا من setNativeProps
بالكامل وستُتَجاوَز.
setNativeProps
وshouldComponentUpdate
يمكنك من خلال تطبيق shouldComponentUpdate
بذكاءٍ تَجنّبُ عمليّات المطابقة غير الضرورية بين أشجار المكونات الفرعية (component subtrees)، إلى النقطة التي قد يكون فيها الأداء كافياً لاستخدام setState
بدلاً من setNativeProps
.
توابع أصيلة أخرى
تتوفر التوابع الموضّحة هنا على معظم المكونات الافتراضية التي يقدمها React Native. ومع ذلك، لاحظ أنها غير متوفرة على المكونات المركّبة التي لا تُدعم مباشرةً من طرف واجهة أصيلة. سيتضمن هذا عمومًا معظم المكونات التي تُعرِّفها في تطبيقك الخاص.
measure(callback)
تُحدِّد الموقع على الشاشة والعرض والارتفاع للواجهة المحددة في الإطار المعروض، وتُعيد القيم عبر دالّة رد نداء غير متزامنة (async callback). في حالة نجاح ذلك، ستُستدعَى دالّة رد النّداء باستخدام المعاملات التالية:
- x
- y
- width
- height
- pageX
- pageY
لاحظ أن هذه القياسات غير متوفّرة إلا بعد اكتمال التصيير على الجهة الأصيلة. إذا كنت بحاجة إلى القياسات في أقرب وقت ممكن ولا تحتاج pageX
و pageY
فانظر الخاصيّة onLayout
كبديل.
العرض والارتفاع اللذان يعيدهما ()measure
هما عرض وارتفاع المكون في الإطار المعروض، إذا كنت تريد حجم المكون الحقيقي فانظر استخدام الخاصية onLayout
كبديل.
measureInWindow(callback)
تُحدِّد موقع الواجهة المعطى في النافذة وتُعيد القيم عبر دالّة رد نداء غير متزامنة. إذا تم تضمين واجهة React الجذر (React root view) في واجهة أصيلٍ آخر، فسوف تُعطيك هذه الدالة الإحداثيات المطلقة (absolute coordinates). في حالة نجاح ذلك، ستُستدعَى دالّة رد النّداء باستخدام المعاملات التالية:
- x
- y
- width
- height
measureLayout(relativeToNativeNode, onSuccess, onFail)
مشابهة للدالة measure()
، ولكنّها تقيس الواجهة نسبةً إلى مكوّنٍ جدٍّ (ancestor)، المحدَّد بالاسم relativeToNativeNode
. هذا يعني أن القيمتان x
وy
نسبيّتان نسبةً إلى القيمتين الأصليتين x
وy
الخاصّتين بالواجهة الجد (ancestor view).
ملاحظة: يمكن استدعاء هذه الدالة مع معالج relativeToNativeNode
أيضًا (بدلًا من المرجع)، لكن يهمل هذا الاختلاف.
إليك المثال التالي (تجربة حية):
import React, { useEffect, useRef, useState } from "react";
import { Text, View, StyleSheet } from "react-native";
const App = () => {
const textContainerRef = useRef(null);
const textRef = useRef(null);
const [measure, setMeasure] = useState(null);
useEffect(() => {
if (textRef.current && textContainerRef.current) {
textRef.current.measureLayout(
textContainerRef.current,
(left, top, width, height) => {
setMeasure({ left, top, width, height });
}
);
}
}, [measure]);
return (
<View style={styles.container}>
<View
ref={textContainerRef}
style={styles.textContainer}
>
<Text ref={textRef}>
Where am I? (relative to the text container)
</Text>
</View>
<Text style={styles.measure}>
{JSON.stringify(measure)}
</Text>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "center",
},
textContainer: {
backgroundColor: "#61dafb",
justifyContent: "center",
alignItems: "center",
padding: 12,
},
measure: {
textAlign: "center",
padding: 12,
},
});
export default App;
focus()
تطلب التركيز على المدخل أو الواجهة المعطاة. يعتمد السّلوك الذي سيُطلَق على نظام التشغيل ونوع الواجهة (view type).
blur()
تُزيل التركيز على المدخل أو الواجهة المعطى. وهي عكس focus()
.