الغرض من هذا البرنامج التعليمي هو إرشادك في الخطوات الأولى مع React Native.
دعنا نبني تطبيقًا باستخدام واجهة برمجة تطبيقات Maze TV واتباع النموذج الأولي أدناه:

هذا البرنامج التعليمي قيد الإنشاء. حتى الآن ، يغطي النقاط التالية:
مستودع مع الكود جاهز ، أيضًا قيد الإنشاء : https://github.com/erbuen/favtvshow :)
قبل البدء في كتابة التعليمات البرمجية ، من المهم فهم السلوكيات المرتبطة بكل شاشة.
هذه هي الشاشة التي سيشاهدها المستخدم عند بدء تشغيل التطبيق.

يجب أن يكون لديها شريط بحث ، وعلى الجانب الأيمن من الشريط ، زر.
يجب أن يحتوي شريط البحث على عنصر نائب. عند إدخال كلمة يجب البحث عنها ، يجب أن نستخدم الزر الذي تتيحه لوحة المفاتيح لبدء البحث ، على سبيل المثال ، كيف نتحدث عند كتابة عنوان URL في متصفح الهاتف المحمول.
العثور على النتائج ، يجب عرضه أسفل شريط البحث وتحت عنوان "نتيجة البحث" ، واحد تلو الآخر. إذا لم تحصل على أي نتائج ، فيجب عليك عرض الرسالة "لم يتم العثور على نتائج".
يجب عرض النتائج على أنها "بطاقات" تحتوي على الصورة واسم السلسلة والأنواع المنسوبة إليها (انظر القسم 1.3).
عندما يختار المستخدم إحدى النتائج ، يجب نقله إلى شاشة أخرى تعرض المعلومات الكاملة للسلسلة. سنصف هذه الشاشة قريبًا.
يجب أن يعرض الزر الأيمن لشريط البحث ، عند النقر عليه ، السلسلة المفضلة للمستخدم أسفل عنوان "المفضلات".
إذا لم يكن هناك بحث قيد التقدم ، فلا تعرض أي شيء على الشاشة.
إذا لم تكن هناك سلسلة مفضلة ، فاعرض رسالة "لا يزال ليس لديك سلسلة مفضلة".
عندما ينقر المستخدم على سلسلة ، إما في نتائج البحث أو لصالح ، يجب أن يتم نقله إلى هذه الشاشة.

يجب أن تعرض هذه الشاشة صورة مرتبطة بالمسلسل والاسم والأنواع والملخص.
بجانب الاسم والأنواع ، يجب أن يكون لديك زر حتى يتمكن المستخدم من تفضيل السلسلة.
عندما ينقر المستخدم على الزر لصالح السلسلة ، يجب حفظ البيانات في التخزين العالمي الداخلي للتطبيق ويجب أن يكون هناك بعض العودة المرئية إلى المستخدم الذي يمكن أن يكون "نخب" أو ما شابه.
فوق صورة السلسلة ، يجب أن يكون لديك زر إرجاع سيأخذ إلى الشاشة السابقة.
يجب أن يكون المستخدم قادرًا على لف الشاشة لأن محتويات الملخص قد تكون أكبر من حجم شاشة الجهاز.
سترى قائمة سلسلة عند البحث والحصول على نتيجة واحدة على الأقل أو عند الوصول إلى سلسلتك المفضلة.
لهذا الغرض ، يجب أن نبني مكون بطاقة كصورة أدناه:

يجب النقر فوق البطاقات وعرضها على قائمة مسطحة.
لقد حان الوقت لبدء المشروع! للقيام بذلك ، تفضل بزيارة صفحة React Native وانقر فوق "ابدأ" .

في صفحة البدء ، ستجد جميع المعلومات التي تحتاجها لبدء React Native. لهذا البرنامج التعليمي ، نقترح عليك اختيار خيار React Native ClickStart .

ثم اختر الخيار الأفضل الذي يناسب ملفك الشخصي. في حالتي ، سأتطور باستخدام جهاز Mac ، لذلك اخترت ذلك ، واختبرهم ، كما سأختبر استخدام iPhone ، في الهدف ، اخترت الهدف كـ iOS ، لكنني كنت قد اخترت Android .

يمكنك فقط اختيار الهدف كـ iOS إذا كنت تستخدم Mac. بالنسبة لنظام التشغيل Windows و Linux ، يجب أن تختار بالضرورة Android .
بعد تحديد الخيارات وفقًا لنظام التشغيل الخاص بك ، اتبع التعليمات لتثبيت المرافق اللازمة لاستخدام React Native.
بعد الانتهاء من تثبيت جميع التبعيات وفقًا لخطوة خطوة لموقع React الأصلي ، ستتمكن من تشغيل مشروعك مع الأمر التالي:
npx react-native init NomeDoProjetoيمكن أن يستغرق إنشاء المشروع بضع دقائق ، ولا تقلق واتبع السجلات من خلال المحطة :)
عند اكتمال العملية ، يمكنك تشغيل مشروعك بالأوامر التالية:
cd NomeDoProjeto
npx react-native run-iosإذا كنت تستخدم MacOS. أو ، إذا كنت تستخدم Windows أو Linux:
cd NomeDoProjeto
npx react-native run-androidبعد فترة من الوقت - وفي المرة الأولى التي ندير فيها هذا الأمر ، يمكن أن يستغرق هذا الأمر وقتًا طويلاً - سترى شيئًا كهذا (اعتمادًا على نظام التشغيل المستهدف ):

هام: لاحظ أن نص المشروع الافتراضي في أول تنفيذ (بدون تغييرات) يذكر خيارات لك لعرض التغييرات الخاصة بك (إعادة التحميل) وأيضًا تصحيحها. هذا يعني أنه يمكنك تغيير الكود ومشاهدة التغييرات وإجراء تصحيح في الوقت الفعلي.
لكي تعدل المشروع ، يجب عليك استخدام محرر رمز من اختيارك:
عند فتح المشروع في محرر التعليمات البرمجية التي يختارونها ، سترى هذا المجلد وهيكل الملف:

ملاحظة: هذا هو الشريط الجانبي لرمز Visual Studio الخاص بي. أنا أستخدم موضوعًا مختلفًا عن المعيار ولدي أيضًا امتداد يعدل صور الرموز المرتبطة بكل ملف أو مجلد. الاسم الذي أعطيته لمشروعي هو favtvshow .
ما هو المهم أن تعرف هنا:
تحتوي مجلدات Android و iOS على الكود الأصلي الذي تم إنشاؤه. يمكنك تشغيل التطبيق الخاص بك على Android Studio أو Xcode فتح هذه المجلدات في كل IDE على التوالي. هذه المجلدات مهمة لإنشاء إصدار التطبيق الخاص بك.
يحتوي مجلد Node_Modules على جميع مرافق المشروع المثبتة بواسطة NPM.
يمكن تحرير التطبيق في البداية من خلال ملف app.js.
يبحث ملف index.js ويسجل المكون العالمي لتطبيقنا ، أي أول مكون يتم تحميله. إنه يهم محتويات ملف app.js وتقديمها على الشاشة.
يحتوي ملف package.json على جميع البيانات الموجودة في المبنى وأيضًا من البرامج النصية المتعلقة بمشروعك.
احذف جميع محتويات ملف app.js واستبداله بـ:
import React , { Component } from 'react' ; // 1
import { Text , View , StyleSheet } from 'react-native' ; // 2
// 3
export default class App extends Component {
render ( ) {
return (
// 4
< View style = { styles . hello } >
< Text > Hello, world! </ Text >
</ View >
) ;
}
}
// 5
const styles = StyleSheet . create ( {
hello : {
flex : 1 ,
justifyContent : 'center' ,
alignItems : 'center' ,
} ,
} ) ;عند إزالة محتوى App.js ، ضع هذا المحتوى الجديد وحفظ الملف ، إذا كان لديك جهاز محاكاة ، فإنه يعيد شحن العرض تلقائيًا.
تأكد من قراءة الوثائق الرسمية حول النص وعرض مكونات ورقة التجريد.
دعنا نغير نمط النص قليلاً في مكون النص . أولاً ، أعد كتابة خط خط النص بحيث يكون مثل هذا:
< Text style = {styles.text} > Hello, world! </ Text >ثم أضف نمط النص في الأنماط . سيكون هكذا:
const styles = StyleSheet . create ( {
hello : {
flex : 1 ,
justifyContent : 'center' ,
alignItems : 'center' ,
} ,
text : {
fontSize : 30 ,
color : 'blue' ,
} ,
} ) ;انظر النتيجة في المحاكاة عند حفظ الملف! سائدا
نحتاج إلى البدء في إعطاء مشروعنا وجهًا وفقًا للنموذج الأولي. سنفعل هذا ببساطة قدر الإمكان ، على الأقل في البداية.
كيف يمكننا إضافة العناصر اللازمة للشاشة الرئيسية؟ واحد منهم هو شريط البحث. هناك مكتبات بها هذا المكون الجاهز ، ولكن حتى نتمكن من تعلم كيفية القيام بذلك ، لن نستخدمها.
دعنا نضيف مكون TextInput إلى سطر الاستيراد الثاني الخاص بنا. انظر وثائق هذا المكون هنا.
import { Text , View , StyleSheet , TextInput } from 'react-native' ;دعنا نعدل مقطع الكود الذي يهتم بتقديم الشاشة لتكون مثل هذا:
export default class App extends Component {
render ( ) {
return (
< View style = { styles . screen } >
< View style = { styles . search } >
< TextInput style = { styles . input } />
</ View >
< View style = { styles . results } >
< Text > Os resultados aparecerão aqui </ Text >
</ View >
</ View >
) ;
}
}لدينا مكون عرض يلف جميع عناصر الشاشة ويتلقى نمط الشاشة .
ضمن هذا المكون ، لدينا مكونان آخران للعرض : واحد في الجزء العلوي (الخلفية البيضاء) التي تتلقى نمط البحث وواحد أدناه (رمادي فاتح مؤقتًا للتصور) الذي يتلقى النتائج .
ضمن المكون العلوي ، لدينا مكون TextView . وداخل المكون في أسفل الشاشة ، لدينا مكون النص .
الآن دعنا نعدل الأنماط بحيث تكون بهذه الطريقة:
const styles = StyleSheet . create ( {
screen : {
flex : 1 ,
flexDirection : 'column' ,
} ,
search : {
flex : 1 ,
justifyContent : 'center' ,
alignItems : 'center' ,
} ,
input : {
marginTop : 55 ,
height : 40 ,
width : 250 ,
borderColor : 'lightgray' ,
borderWidth : 1 ,
padding : 10 ,
fontSize : 20 ,
} ,
results : {
flex : 4 ,
backgroundColor : 'lightgray' ,
alignItems : 'center' ,
} ,
} ) ;قم بإجراء التغييرات وحفظ الملف. انظر الآن التفسير لكل عنصر:
مكون العرض الذي يشمل جميع المكونات الأخرى (نمط اسم الشاشة ) له مرن يساوي 1. وهذا يجعلها كل الشاشة ، حيث أنها تشمل جميع الآخرين. له اتجاه مرن يساوي العمود لأننا نريد أن تنظم المكونات الموجودة داخلها رأسياً.
لدينا مكون طريقة العرض -Style -Style للأعلى والنتائج مع النتائج التي تبقى أدناه. فهي داخل مكون العرض مع نمط الشاشة وتنظم عموديًا. لديهم لتقسيم نفس المساحة. نحن نفعل هذا باستخدام المرن. يحتوي الجزء العلوي على Flex 1 و Bottom 4. وهذا يعني أن لدينا 5 أجزاء متناسبة (1+4) ، والتي لديها ثنية تساوي 1 تحتل 1/5 والمرونة التي تساوي 4 يحتل 4/5. هام: انظر React Native Documentation لمعرفة المزيد عن FlexBox :)
بالنظر إلى مكون عرض نمط البحث ، في الداخل ، نضع مكون TextView المخصص باستخدام نمط الإدخال . مع ذلك ، تمكنا من تحديد ارتفاعه (الارتفاع) ، العرض (العرض) ، المسافة من الهامش العلوي من الشاشة (Margintop) ، لون الألوان الحدودية ، سماكة الحدود ، للخلف (الحشو) وحجم مصدر الحقل (الخط).
في جهاز المحاكاة ، إذا قمت بالنقر فوق TextInput ، تظهر لوحة المفاتيح تلقائيًا. يمكنك كتابة شيء ما والنقر فوق "العودة" (في حالة iOS). في الوقت الحالي ، لن يحدث شيء لأننا ما زلنا بحاجة إلى تنفيذ السلوك.
دعنا نضيف متغيرًا يسمى الحالة قبل دالة Render () التي ستتلقى النص المكتوب في textinput .
state = {
searchText : '' ,
}سنقوم أيضًا بتغيير TextInput للحصول على عنصر نائب وحفظه في متغير الحالة النص المكتوب. سيكون هكذا:
< TextInput
placeholder = { 'Procure uma série' }
style = { styles . input }
onChangeText = { ( text ) => this . setState ( { searchText : text } ) }
/>لاحظ طريقة OnChangetext للمكون. يتلقى قيمة النص وحفظه (this.setState) في SearchText . يمكننا التحقق من ذلك في الوقت الفعلي لإجراء تغيير بسيط في مكون آخر.
حيث لدينا:
< Text > Os resultados aparecerão aqui </ Text >التغيير من أجل:
< Text > {this.state.searchText} </ Text >احفظ واختبار كتابة شيء ما في TextInput :)
تغيير TextInput الخاص بنا أكثر قليلاً ، دعنا نستخدم طريقة OnSubmitediting لإجراء البحث عندما يضغط المستخدم على مفتاح الإرجاع (أو مكافئ على Android). في الوقت الحالي ، لن نقدم أي طلب إلى واجهة برمجة التطبيقات حتى الآن ، لكننا سنترك الأشياء المُحال!
سيكون هذا النص على هذا النحو:
< TextInput
placeholder = { 'Procure uma série' }
style = { styles . input }
onChangeText = { ( text ) => this . setState ( { searchText : text } ) }
onSubmitEditing = { ( ) => this . submitSearch ( ) }
/>نحتاج إلى إضافة وظيفة submitsearch () ، ويمكن القيام بذلك أسفل الحالة مباشرة. سيكون هكذا:
state = {
searchText : '' ,
}
submitSearch ( ) {
alert ( 'Buscar: ' + this . state . searchText ) ;
}نظرًا لأننا لا نقدم الطلب بعد ، فأنا أضع تنبيهًا حتى تدرك أنه سيتم استخدام المحتوى المكتوب في حقل النص في البحث ، حيث سيحدث داخل وظيفة SubmitSearch () .
حتى نتمكن من شعبية تطبيقنا مع البيانات ، دعنا نستخدم متاهة API TV وهي واجهة برمجة تطبيقات REST مفتوحة (لا نحتاج إلى المصادقة) ، مجانًا وإرجاع البيانات بتنسيق JSON.
دعونا نلقي نظرة على مثال بحث باستخدام واجهة برمجة التطبيقات هذه. إذا أردنا إجراء بحث باستخدام Word Anatomy ، فسنستخدم نقطة النهاية التالية:
[GET] http://api.tvmaze.com/search/shows?q=anatomy
نتيجة لذلك ، سيكون لدينا JSON أدناه (يتم عرض امتداد واحد فقط) يحتوي على جميع الإدخالات التي لها تشريح أو شيء من هذا القبيل:
[
{
"score" : 20.919525 ,
"show" : {
"id" : 67 ,
"url" : " http://www.tvmaze.com/shows/67/greys-anatomy " ,
"name" : " Grey's Anatomy " ,
"type" : " Scripted " ,
"language" : " English " ,
"genres" : [
" Drama " ,
" Romance " ,
" Medical "
],
"status" : " Running " ,
"runtime" : 60 ,
"premiered" : " 2005-03-27 " ,
"officialSite" : " http://abc.go.com/shows/greys-anatomy/ " ,
"schedule" : {
"time" : " 21:00 " ,
"days" : [
" Thursday "
]
},
"rating" : {
"average" : 8.3
},
"weight" : 99 ,
"network" : {
"id" : 3 ,
"name" : " ABC " ,
"country" : {
"name" : " United States " ,
"code" : " US " ,
"timezone" : " America/New_York "
}
},
"webChannel" : null ,
"externals" : {
"tvrage" : 3741 ,
"thetvdb" : 73762 ,
"imdb" : " tt0413573 "
},
"image" : {
"medium" : " http://static.tvmaze.com/uploads/images/medium_portrait/211/529884.jpg " ,
"original" : " http://static.tvmaze.com/uploads/images/original_untouched/211/529884.jpg "
},
"summary" : " <p>The doctors of Grey Sloan Memorial Hospital deal with life-or-death consequences on a daily basis -- it's in one another that they find comfort, friendship and, at times, more than friendship. Together they're discovering that neither medicine nor relationships can be defined in black and white. Real life only comes in shades of grey.</p> " ,
"updated" : 1576320037 ,
"_links" : {
"self" : {
"href" : " http://api.tvmaze.com/shows/67 "
},
"previousepisode" : {
"href" : " http://api.tvmaze.com/episodes/1749376 "
},
"nextepisode" : {
"href" : " http://api.tvmaze.com/episodes/1760391 "
}
}
}
},
{
"score" : 15.932307 ,
"show" : {
"id" : 34388 ,
"url" : " http://www.tvmaze.com/shows/34388/greys-anatomy-b-team " ,
"name" : " Grey's Anatomy: B-Team " ,
"type" : " Scripted " ,
"language" : " English " ,
"genres" : [
" Drama " ,
" Romance " ,
" Medical "
],
"status" : " Ended " ,
"runtime" : 3 ,
"premiered" : " 2018-01-11 " ,
"officialSite" : " http://abc.go.com/shows/greys-anatomy-b-team " ,
"schedule" : {
"time" : " " ,
"days" : [
" Thursday "
]
},
"rating" : {
"average" : null
},
"weight" : 80 ,
"network" : null ,
"webChannel" : {
"id" : 95 ,
"name" : " ABC.com " ,
"country" : {
"name" : " United States " ,
"code" : " US " ,
"timezone" : " America/New_York "
}
},
"externals" : {
"tvrage" : null ,
"thetvdb" : null ,
"imdb" : null
},
"image" : {
"medium" : " http://static.tvmaze.com/uploads/images/medium_portrait/142/355662.jpg " ,
"original" : " http://static.tvmaze.com/uploads/images/original_untouched/142/355662.jpg "
},
"summary" : " <p>A fresh crop of interns face their first day at Grey Sloan Memorial Hospital. Can these new surgeons survive the pressures of high-stakes medicine, intimidating attendings, and cut throat competition?</p> " ,
"updated" : 1526845476 ,
"_links" : {
"self" : {
"href" : " http://api.tvmaze.com/shows/34388 "
},
"previousepisode" : {
"href" : " http://api.tvmaze.com/episodes/1390266 "
}
}
}
}
]يمكنك عرض نسخة أكثر ودية من محتويات JSON باستخدام محرر JSON عبر الإنترنت. لقد تركت حفظ حتى تتمكن من عرض كل شيء! انقر هنا ؛)
في JSON Editor Online ، كان من السهل رؤية أنها عبارة عن صفيف مع 9 كائنات وكل كائن عبارة عن سلسلة.
وفقًا للنموذج الأولي وما قمنا بتنفيذه حتى الآن:
دعنا نضع هذا موضع التنفيذ في القسم التالي باستخدام AxiOS.
نحتاج إلى تثبيت مكتبة Axios في المشروع. سنفعل ذلك عن طريق كتابة الأمر التالي في المحطة:
$ npm install axiosبعد ذلك ، دعنا ننشئ مجلد خدمة وملف API.JS بداخله. دعنا نضع الرمز التالي في الملف:
import axios from 'axios' ; // 1
// 2
const api = axios . create ( {
baseURL : 'http://api.tvmaze.com/' ,
} ) ;
export default api ;في app.js ، دعنا نستورد ملف api.js :
import api from './service/api' ;ودعنا نعدل وظيفة Submitsearch () لتكون مثل هذا:
submitSearch = async ( ) => { // 1
if ( this . state . searchText != '' ) { // 2
try { // 3
const response = await api . get ( '/search/shows' , { // 4
params : { q : this . state . searchText } // 5
} ) ;
alert ( JSON . stringify ( response ) ) ;
} catch ( error ) {
alert ( JSON . stringify ( error ) ) ;
}
}
}ستكون التنبيهات داخل كتلة المحاولة وداخل كتلة الصيد مفيدة لعرض الإجابات التي تم الحصول عليها في الطلب. في وقت آخر ، سوف نستخدم هذه الإجابات بطريقة أخرى :)
تعرف على المزيد حول الوظائف غير المتزامنة هنا وحول الوعود هنا.
لنبدأ في تنفيذ قائمة Flatlist لعرض قائمة نتائج البحث. دعنا نعدل استيرادنا لهذا:
import { Text , View , StyleSheet , TextInput , FlatList } from 'react-native' ;ثم سنقوم بتعديل الحالة ووظيفة Submitsearch () بحيث تكون:
state = {
searchText : '' ,
searchResults : null , // 1
}
submitSearch = async ( ) => {
if ( this . state . searchText != '' ) {
try {
const response = await api . get ( '/search/shows' , {
params : { q : this . state . searchText } ,
} ) ;
this . setState ( { searchResults : response . data } ) ; // 2
} catch ( error ) {
alert ( JSON . stringify ( error ) ) ;
}
}
}يتعين علينا أيضًا تعديل Render () ، وإدخال القائمة المسطحة فيه:
render ( ) {
return (
< View style = { styles . screen } >
< View style = { styles . search } >
< TextInput
placeholder = { 'Procure uma série' }
style = { styles . input }
onChangeText = { ( text ) => this . setState ( { searchText : text } ) }
onSubmitEditing = { ( ) => this . submitSearch ( ) }
/>
</ View >
< View style = { styles . results } >
< FlatList
data = { this . state . searchResults }
renderItem = { ( { item } ) => < Text > { item . show . name } </ Text > }
keyExtractor = { item => item . show . id }
/>
</ View >
</ View >
) ;
}لاحظ أننا نقوم فقط بإعداد اسم السلسلة مع مكون النص . ومع ذلك ، دعنا ننشئ نوع البطاقة هنا ، وفقًا للقسم 1.3.
في المشروع ، سنقوم بإنشاء أكثر من مكون واحد. لتسهيل المؤسسة ، دعنا ننشئ مجلد مكون وسيتم حفظ جميع المكونات التي ننشئها فيه.
ضمن هذا المجلد ، سنقوم بإنشاء ملف يسمى card.js مع المحتوى التالي:
import React , { Component } from 'react' ;
import { Text , View , TouchableOpacity , Image , StyleSheet } from 'react-native' ;
export default class Card extends Component {
render ( ) {
return (
< TouchableOpacity style = { styles . container } >
< View style = { styles . cardView } >
< View >
< Image
style = { styles . image }
source = { { uri : this . props . info . image == null ? 'https://i.ibb.co/YfZFr7k/noimg.png' : ( this . props . info . image . original || this . props . info . image . medium ) } }
/>
</ View >
< View style = { { flexDirection : 'column' } } >
< Text style = { styles . name } > { this . props . info . name || 'Sem nome' } </ Text >
< Text style = { styles . genres } > { this . props . info . genres || 'Sem gênero' } </ Text >
</ View >
</ View >
</ TouchableOpacity >
) ;
}
}
const styles = StyleSheet . create ( {
container : {
padding : 10 ,
} ,
cardView : {
alignItems : 'center' ,
flexDirection : 'row' ,
} ,
image : {
width : 80 ,
height : 120 ,
resizeMode : 'contain' ,
} ,
name : {
fontSize : 20 ,
marginLeft : 10 ,
} ,
genres : {
fontSize : 16 ,
marginLeft : 10 ,
} ,
} ) ;لاحظ أن اسم المكون هو بطاقة وأنني استخدمت صورة لتكون بمثابة عنصر نائب إذا لم يكن للمسلسل عنوان URL صالح.
الآن دعنا نستورد مكوننا الجديد في ملف app.js :
import Card from './components/card' ;وفي قائمة مسطحة سنحل محل المحتوى الحالي بـ:
< FlatList
data = { this . state . searchResults }
renderItem = { ( { item } ) => < Card info = { item . show } /> }
keyExtractor = { item => item . show . id }
/>بدلاً من استخدام مكون النص ، نستخدم الآن البطاقة ونمررها من خلال سمة المعلومات التي تتلقى كائن العرض .
راجع ملف card.js الذي نستخدمه عدة مرات التعبير this.props.info للوصول إلى القيم التي تم تمريرها عند استخدام المكون. للحصول على اسم السلسلة ، نستخدم this.props.info.name ، على سبيل المثال.