یه نقل معروفی هست میگه برو داکشو بخون یاد میگیری ولی در مورد Apollo client تو ری اکت نیتیو اصلا صدق نمیکنه.داکش به شرح زیره:
ابتدای امر بریم کلاینت آپولو و متعلقاتش رو نصب کنیم
yarn add @apollo/client @apollo/react-hooks apollo-cache-inmemory apollo-client apollo-link apollo-upload-client @lifeomic/axios-fetch axios
صب کن ببینم چرا انقدر وابستگی داریم نصب میکنیم؟ متاسفانه یا من یا خود آپلو راه رو درست نرفتیم و کلی به conflict خوردم موقع نصب این شد که همه اینارو باهم نصب میکنیم که آپلو کلاینت به عدم وجود peer dependencies گیر نده.
قبل از اینکه Provider رو بیاریم تو کار بریم ابتدا یک کانفیگ مناسب برای Apollo بنویسیم توجه کنید که دیفالت آپلو خیلی چیزا نداره و کلا دستتون رو باز گذاشته که هرچی دوست دارید بهش پاس بدین به این کد
ابتدا یکم import بازی:
import { ApolloClient, ApolloLink } from "@apollo/client"
import { Observable } from "apollo-link"
import { InMemoryCache } from "apollo-cache-inmemory"
import { } from "@apollo/client/link/error"
import { createUploadLink } from "apollo-upload-client"
import { buildAxiosFetch } from "@lifeomic/axios-fetch"
import { Config } from "App/Config"
import axios from "axios"
import AsyncStorage from "@react-native-community/async-storage"
خب ابتدا باید Apollo Client , Apollo Link رو بیارم کلاینت رو برای ساختن یه instance از کلاینت دومی برای پاس دادن یه سری چیز دیگه پایین تر گفته میشه. Observable که تو هر ریکوئست یه Subscribe میکنه به ریکوئست تا زمانی که هندل بشه. inMemoryCache که وظیفه کش کردن رو برعهده میگیره و اگر جواب کوئری ها یکسان باشه دیگه ری رندرینگ اضافه نخواهید داشت ( از زیبایی های گراف و آپولو). رو داریم که شاید براتون جالب باشه ولی آپولو عین ارور رو بر نمیگردونه صرفا یک 400 میده و میره خونه باید با این بگیم برادر ارور های مارو برگردون بدونیم فازت چیه. UploadLink اختیاریه مال زمانیه که نیاز به اپلود عکس تو سرور داشته باشید. axios? وات د هل؟ آپلولو کلاینت بصورت پیشفرض از fetch استفاده میکنه موقع آپلود عکس بهتره با axios عوض بشه اینم الزامی نیست.
const httpLink = createUploadLink({
uri: SERVER_URL,
credentials: "same-origin",
fetch: buildAxiosFetch(axios, (config, input, init) => ({
...config,
onUploadProgress: init.onUploadProgress,
})),
});
خب معمولا وقتی میخوایم لینک سرور رو بدیم آپلولو میومدیم createHttpLink رو استفاده میکردیم ولی ما میخوایم قابلیت آپلود عکسم داشته باشیم چون createUploadLink همه خاصیت های httpLink رو داره و dim sum (همون یه چی اضافه تر خودمون) fetch رو هم با axios و ساختن یه آرگومان از buildAxiosFetch میدیم اینطوری میتونیم درصد اپلود شدن رو هم با uploadProgress داشته باشیم.
خب سوال پیش میاد پس توکن چی؟ اصلا بخوایم چیزی تو هدر با هر ریکوئست بنویسیم چیکار کنیم؟
const request = async (operation) => {
const token = await AsyncStorage.getItem("token");
console.log(token);
operation.setContext({
headers: {
authorization: token ? `Bearer ${token}` : "",
},
});
};
فکر کنم نیاز به توضیح نداشته باشه. کانتکست توی گراف تو هر resolver اجرا میشه اینطوری تو هر ریکوئست میتونین توکن رو پاس بدین و اینکه اگر دوست دارین خط لاگ کردن توکن رو بردارین.
export const requestLink = new ApolloLink(
(operation, forward) =>
new Observable((observer) => {
let handle;
Promise.resolve(operation)
.then((oper) => request(oper))
.then(() => {
handle = forward(operation).subscribe({
next: observer.next.bind(observer),
error: observer.error.bind(observer),
complete: observer.complete.bind(observer),
});
})
.catch(observer.error.bind(observer));
return () => {
if (handle) handle.unsubscribe();
};
})
);
اینجا همون داستان سابسکرایب کردن به ریکوئست ها و برگردوندن عملیات بعدی تا اینکه همه چی تموم بشه و ما آنسابسکرایب کنیم.
const link = ApolloLink.from([
(({ graphQLErrors, networkError }) => {
if (graphQLErrors) {
console.log("[graphQLErrors]", graphQLErrors);
graphQLErrors.map(({ message, extensions }) => {
console.log(
`[GraphQL error]: Message: ${message}, code: ${extensions.code}`
);
if (extensions.code === "UNAUTHENTICATED") {
AsyncStorage.clear();
}
});
}
if (networkError) {
console.log(`[Network error]: ${networkError}`);
}
}),
requestLink,
httpLink,
]);
اینم برای هندل کردن ارور ها داریم میگیم لینک ریکوئست و Http مارو بگیر ریکوئست بزن اگر ارور Network داشت برش گردون اگر ارور دیگه ای داشت برش گردون با کد ارور و پیامش و در آخر اگر ارور از نوع UNAUTHENTICATED بود یعنی توکن احتمالا معتبر نیست استوریج رو پاک کن و ارور رو برگردون.
const client = new ApolloClient({ link, cache });
export { client };
خب در نهایت لینک و کش که ساختیم رو پاس میدیم به یه Instance از کلاینت آپولو و export اش میکنیم برای استفاده در اپلیکیشن.
خب قدم بعدی اینه که بیایم یه instance از اپ با آپولو بسازیم و اون رو رجیستر کنیم پس میریم داخل index.js
import React from "react"
import { AppRegistry } from "react-native"
import RootApp from "./App/App"
import { name as appName } from "./app.json"
import { ApolloProvider } from "@apollo/react-hooks"
import { client } from "./App/Api/ApolloConfig"
const App = () => (
<ApolloProvider client={client}>
<RootApp />
</ApolloProvider>
);
AppRegistry.registerComponent(appName, () => App);
حالا یه کوئری و یک mutation بنویسید و تست کنید امیدوارم که جواب بده ( فقط یه نکته مهم در ایمولاتر آندروید کلا جواب نمیده دیوایس واقعی رو تست کنید) ( کوئری ها همیشه اوکیه با mutation تست کنید درست بود یعنی اوکیه)