الاتصال بالشبكة في React Native
تحتاج العديد من تطبيقات الجوال إلى تحميل الموارد من عنوان URL بعيد، فقد ترغب بإرسال طلب POST إلى واجهة REST برمجيّة، أو قد تحتاج ببساطة إلى جلب محتوًى ثابت من خادم آخر.
استخدام Fetch
يوفر React Native واجهة Fetch البرمجية للاتصال بالشبكة. ستكون الواجهة Fetch مألوفة بالنسبة لك إن استخدمت XMLHttpRequest
أو واجهات التشبيك البرمجية الأخرى سابقًا. يمكنك الرجوع إلى مقال حول كيفيّة استخدام Fetch في أكاديمية حسوب للحصول على معلومات إضافية.
إنشاء الطلبات
لجلب محتوًى من عنوان URL مُعيّن، مرِّره للدالة fetch()
ببساطة كما يلي:
fetch('https://mywebsite.com/mydata.json');
تقبل الدالة fetch()
كذلك معاملًا ثانيًّا اختياريًّا يسمح لك بتخصيص طلب HTTP الذي تُريد إرساله، قد ترغب في تحديد ترويسات (headers) إضافية أو تقديم طلب POST عوضًا عن طلب GET:
fetch('https://mywebsite.com/endpoint/', {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
firstParam: 'yourValue',
secondParam: 'yourOtherValue'
})
});
ألقِ نظرة على توثيق Fetch Request لقائمة كاملة بالخاصيات الممكنة.
التعامل مع الاستجابة
توضح الأمثلة أعلاه كيفيّة إرسال الطلبات، لكنّك ستحتاج إلى التّعامل مع الرّد في كثير من الحالات.
التشبيك عملية غير متزامنة (asynchronous) بطبيعتها. تعيد توابع Fetch وعدًا (Promise) يسهّل كتابة شيفرة برمجية تعمل بطريقة غير متزامنة:
const getMoviesFromApi = () => {
return fetch('https://reactnative.dev/movies.json')
.then((response) => response.json())
.then((json) => {
return json.movies;
})
.catch((error) => {
console.error(error);
});
};
يمكنك أيضًا استخدام صيغة async
/await
في تطبيقات React Native:
const getMoviesFromApiAsync = async () => {
try {
let response = await fetch(
'https://reactnative.dev/movies.json'
);
let json = await response.json();
return json.movies;
} catch (error) {
console.error(error);
}
};
ملاحظة: لا تنسَ إمساك (catch) أي أخطاء قد ترميها fetch
، وإلا سترمى هذه الأخطاء بصمتٍ.
import React, { useEffect, useState } from 'react';
import { ActivityIndicator, FlatList, Text, View } from 'react-native';
export default App = () => {
const [isLoading, setLoading] = useState(true);
const [data, setData] = useState([]);
useEffect(() => {
fetch('https://reactnative.dev/movies.json')
.then((response) => response.json())
.then((json) => setData(json.movies))
.catch((error) => console.error(error))
.finally(() => setLoading(false));
}, []);
return (
<View style={{ flex: 1, padding: 24 }}>
{isLoading ? <ActivityIndicator/> : (
<FlatList
data={data}
keyExtractor={({ id }, index) => id}
renderItem={({ item }) => (
<Text>{item.title}, {item.releaseYear}</Text>
)}
/>
)}
</View>
);
};
import React, { Component } from 'react';
import { ActivityIndicator, FlatList, Text, View } from 'react-native';
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
data: [],
isLoading: true
};
}
componentDidMount() {
fetch('https://reactnative.dev/movies.json')
.then((response) => response.json())
.then((json) => {
this.setState({ data: json.movies });
})
.catch((error) => console.error(error))
.finally(() => {
this.setState({ isLoading: false });
});
}
ملاحظة: تمنع منصّة iOS افتراضيًّا أي طلبٍ غير مشفّر باستخدام SSL. إن احتجت إلى جلب بيانات من عنوان URL غير مُشفّر (الذي يبدأ بالمقطع http
عوضًا عن https
) ، فستحتاج أولا إلى إضافة استثناء App Transport Security . إذا كنت تعرف مسبقًا النطاقات التي ستحتاج إلى الوصول إليها، فمن الأفضل إضافة استثناءات لهذه النطاقات فقط؛ إذا لم تكن النطاقات معروفة حتى وقت تشغيل التطبيق فيمكنك تعطيل ATS بالكامل. ومع ذلك لاحظ أنه اعتبارًا من يناير 2017 تتطلب مراجعة سوق Apple للتطبيقات تبريرات معقولة لتعطيل ATS. راجع توثيق Apple لمزيد من المعلومات.
أما على منصة Android فتمنع حركة مرور النص غير المشفر بشكلٍ افتراضيًّ اعتبارًا من الواجهات البرمجية من المستوى 28، يمكن تغيير هذا السلوك بضبط android:usesCleartextTraffic
في ملف بيان (manifest file) التطبيق.
استعمال مكتبات تشبيك أخرى
ضُمّنت واجهةُ XMLHttpRequest مسبقًا في React Native، ما يعني أنّك تستطيع استخدام مكتباتِ خارجية (third party libraries) مثل frisbee أو axiosالتي تعتمد عليها، أو يمكنك استخدام XMLHttpRequest
مباشرة إذا كنت تفضل ذلك.
var request = new XMLHttpRequest();
request.onreadystatechange = (e) => {
if (request.readyState !== 4) {
return;
}
if (request.status === 200) {
console.log('success', request.responseText);
} else {
console.warn('error');
}
};
request.open('GET', 'https://mywebsite.com/endpoint/');
request.send();
ملاحظة: يختلف نموذج الأمان الخاص بواجهة XMLHttpRequest
عن ذلك الموجود على الويب، إذ لا يوجد مفهوم CORS في التطبيقات الأصيلة.
دعم WebSocket
يدعم React Native بروتوكول WebSockets كذلك، والذي يوفر قنوات اتصال ثنائية الاتجاه (full-duplex communication) عبر اتصال TCP واحد.
var ws = new WebSocket('ws://host.com/path');
ws.onopen = () => {
// فتح الاتصال
ws.send('something'); // send a message
};
ws.onmessage = (e) => {
// استقبلت رسالة
console.log(e.data);
};
ws.onerror = (e) => {
// حدث خطأ
console.log(e.message);
};
ws.onclose = (e) => {
// أغلق الاتصال
console.log(e.code, e.reason);
};
المشكلات المعروفة في fetch والمصادقة المعتمدة على ملفات تعريف الارتباط (cookies)
لا تعمل الخيارات التالية في fetch:
redirect:manual
credentials:omit
- عند وجود ترويسات لها الاسم نفسه في Android تؤخذ الأخيرة فقط، يمكن إيجاد حل مؤقت في https://github.com/facebook/react-native/issues/18837#issuecomment-398779994.
- المصادقة المعتمدة على ملفات تعريف الارتباط (cookies) غير مستقرة حاليًا، يمكنك مشاهدة أهم المشكلات في https://github.com/facebook/react-native/issues/23185,
- عند إعادة التوجيه في iOS باستخدام
302
-كحدٍّ أدنى- وإذا كانت الترويسةSet-Cookie
موجودة فلن يعيَّن ملف تعريف الارتباط بشكلٍ صحيح، وبما أن إعادة التوجيه لا تعالج بشكلً يدويٍَ فيمكن أن ينتج سيناريو حدوث طلبات لا نهائية إذا كانت إعادة التوجيه نتيجة جلسة منتهية الصلاحية.
ضبط NSURLSession في iOS
قد تتطلب بعض التطبيقات توفير ضبط NSURLSessionConfiguration
خاص من أجل NSURLSession
المستخدم في إجراء إرسال طلبيات عبر الشبكة في تطبيقات React Native على نظام iOS. مثلًا، قد تحتاج إلى ضبط عميل مستخدم user agent خاص لجميع طلبيات الشبكة القادمة من تطبيق أو مزود NSURLSession
مع ضبط NSURLSessionConfiguration
مؤقت، وهنا تسمح الدالة RCTSetCustomNSURLSessionConfigurationProvider
بإجراء مثل هذا الضبط المخصص. تذكر بإضافة الاستيراد التالي إلى الملف الذي ستستدعي فيه الدالة RCTSetCustomNSURLSessionConfigurationProvider
:
#import <React/RCTHTTPRequestHandler.h>
يجب أن تُستدعى الدالة RCTSetCustomNSURLSessionConfigurationProvider
في أول دورة حياة التطبيق وبذلك تكون متاحة دومًا متى ما دعت الحالة لاستعمالها من طرف React. انظر الشيفرة التالية مثلًا:
-(void)application:(__unused UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// set RCTSetCustomNSURLSessionConfigurationProvider
RCTSetCustomNSURLSessionConfigurationProvider(^NSURLSessionConfiguration *{
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
// configure the session
return configuration;
});
// set up React
_bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions];
}