فهم GraphQl من جانب العميل باستخدام Apollo-Client في تطبيقات React

نشرت: 2022-03-10
ملخص سريع ↬ هل حاولت يومًا التفاعل مع خادم GraphQL في تطبيق من جانب العميل وشعرت بالرغبة في الاستسلام حتى قبل الوصول إلى أي مكان؟ هل رفضت يومًا دعوة للانضمام إلى قاعدة رموز تتطلب العمل مع GraphQL API لأنه ليس لديك أي فكرة؟ هل شعرت يومًا بأنك مهندس الواجهة الأمامية الوحيد الذي لم يتعلم كيفية استهلاك واجهات برمجة تطبيقات GraphQL؟ إذا أجبت بنعم على أي من هذه الأسئلة ، فهذا البرنامج التعليمي يناسبك. سنلقي نظرة فاحصة على بعض أساسيات GraphQL و Apollo Client ، بالإضافة إلى كيفية العمل مع كليهما. في النهاية ، سنكون قد أنشأنا تطبيقًا لمتجر الحيوانات الأليفة يستخدم Apollo Client. بعد ذلك ، يمكنك المضي قدمًا في بناء مشروعك التالي.

وفقًا لـ State of JavaScript 2019 ، يرغب 38.7٪ من المطورين في استخدام GraphQL ، بينما يرغب 50.8٪ من المطورين في تعلم GraphQL.

نظرًا لكونها لغة استعلام ، فإن GraphQL تبسط سير العمل في بناء تطبيق العميل. يزيل تعقيد إدارة نقاط نهاية API في تطبيقات جانب العميل لأنه يعرض نقطة نهاية HTTP واحدة لجلب البيانات المطلوبة. ومن ثم ، فإنه يزيل الإفراط في الجلب للبيانات ونقص الجلب ، كما هو الحال في REST.

لكن GraphQL هي مجرد لغة استعلام. من أجل استخدامها بسهولة ، نحتاج إلى منصة تقوم برفع الأحمال الثقيلة نيابة عننا. إحدى هذه المنصات هي أبولو.

منصة Apollo عبارة عن تطبيق لـ GraphQL ينقل البيانات بين السحابة (الخادم) إلى واجهة المستخدم الخاصة بتطبيقك. عند استخدام عميل Apollo ، يتم تغليف كل منطق استرداد البيانات وتتبع وتحميل وتحديث واجهة المستخدم بواسطة خطاف useQuery (كما في حالة React). ومن ثم ، فإن جلب البيانات هو إعلاني. كما أن لديها ذاكرة تخزين مؤقت صفرية التكوين. فقط من خلال إعداد Apollo Client في تطبيقك ، ستحصل على ذاكرة تخزين مؤقت ذكية خارج الصندوق ، دون الحاجة إلى تكوين إضافي.

Apollo Client قابل للتشغيل البيني أيضًا مع أطر أخرى ، مثل Angular و Vue.js و React.

ملاحظة : سيفيد هذا البرنامج التعليمي أولئك الذين عملوا مع RESTful أو أشكال أخرى من واجهات برمجة التطبيقات في الماضي من جانب العميل ويرغبون في معرفة ما إذا كانت GraphQL تستحق التجربة. هذا يعني أنه كان يجب عليك العمل مع API من قبل ؛ عندها فقط ستتمكن من فهم مدى فائدة GraphQL لك. بينما سنغطي بعض أساسيات GraphQL و Apollo Client ، فإن المعرفة الجيدة بـ JavaScript و React Hooks ستكون مفيدة.

أساسيات GraphQL

هذه المقالة ليست مقدمة كاملة لـ GraphQL ، لكننا سنحدد بعض الاصطلاحات قبل المتابعة.

ما هي GraphQL؟

GraphQL هي مواصفة تصف لغة استعلام تعريفية يمكن لعملائك استخدامها لطلب البيانات الدقيقة التي يريدونها من واجهة برمجة التطبيقات. يتم تحقيق ذلك من خلال إنشاء مخطط نوع قوي لواجهة برمجة التطبيقات (API) الخاصة بك ، مع مرونة مطلقة. كما أنه يضمن أن API يحل البيانات وأن استعلامات العميل يتم التحقق منها مقابل المخطط. يعني هذا التعريف أن GraphQL تحتوي على بعض المواصفات التي تجعلها لغة استعلام تعريفية ، مع واجهة برمجة تطبيقات مكتوبة بشكل ثابت (مبنية حول Typescript) وتجعل من الممكن للعميل الاستفادة من أنظمة النوع هذه ليطلب من واجهة برمجة التطبيقات البيانات الدقيقة التي يريدها .

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

دعنا نلقي نظرة على بعض اصطلاحات GraphQl التي ستساعدنا ونحن نواصل.

أساسيات

  • عمليات
    في GraphQL ، يُطلق على كل إجراء يتم تنفيذه عملية. هناك عدد قليل من العمليات وهي:
    • استفسار
      تختص هذه العملية بجلب البيانات من الخادم. يمكنك أيضًا تسميته إحضار للقراءة فقط.
    • طفره
      تتضمن هذه العملية إنشاء وتحديث وحذف البيانات من الخادم. يطلق عليه شعبيا عملية CUD (إنشاء وتحديث وحذف).
    • الاشتراكات
      تتضمن هذه العملية في GraphQL إرسال البيانات من الخادم إلى عملائها عند وقوع أحداث معينة. وعادة ما يتم تنفيذها مع WebSockets.

في هذه المقالة ، سنتعامل فقط مع عمليات الاستعلام والطفرات.

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

ما المقصود بـ GraphQL من جانب العميل؟

عندما يقوم مهندس الواجهة الأمامية ببناء مكونات واجهة المستخدم باستخدام أي إطار عمل ، مثل Vue.js أو (في حالتنا) React ، يتم نمذجة هذه المكونات وتصميمها من نمط معين على العميل لتناسب البيانات التي سيتم جلبها من الخادم.

واحدة من أكثر المشاكل شيوعًا في RESTful APIs هي الإفراط في الجلب ونقص الجلب. يحدث هذا لأن الطريقة الوحيدة التي يمكن للعميل من خلالها تنزيل البيانات هي ضرب نقاط النهاية التي تُرجع هياكل البيانات الثابتة . الجلب الزائد في هذا السياق يعني أن العميل يقوم بتنزيل معلومات أكثر مما هو مطلوب من قبل التطبيق.

من ناحية أخرى ، في GraphQL ، يمكنك ببساطة إرسال استعلام واحد إلى خادم GraphQL يتضمن البيانات المطلوبة. سيستجيب الخادم بعد ذلك بكائن JSON للبيانات الدقيقة التي طلبتها - وبالتالي ، لا يوجد زيادة في الجلب. يوضح سيباستيان إيشويلر الاختلافات بين واجهات برمجة تطبيقات RESTful و GraphQL.

تعد GraphQL من جانب العميل بنية أساسية من جانب العميل تتفاعل مع بيانات من خادم GraphQL لأداء الوظائف التالية:

  • يدير البيانات عن طريق إرسال الاستعلامات وتغيير البيانات دون الحاجة إلى إنشاء طلبات HTTP بنفسك. يمكنك قضاء وقت أقل في بيانات السباكة ومزيد من الوقت في إنشاء التطبيق الفعلي.
  • إنه يدير تعقيد ذاكرة التخزين المؤقت نيابة عنك. لذلك ، يمكنك تخزين واسترداد البيانات التي تم جلبها من الخادم ، دون أي تدخل من طرف ثالث ، وتجنب إعادة جلب الموارد المكررة بسهولة. وبالتالي ، فإنه يحدد متى يكون هناك مصدران متماثلان ، وهو أمر رائع بالنسبة لتطبيق معقد.
  • إنها تحافظ على توافق واجهة المستخدم الخاصة بك مع Optimistic UI ، وهي اتفاقية تحاكي نتائج الطفرة (أي البيانات التي تم إنشاؤها) وتقوم بتحديث واجهة المستخدم حتى قبل تلقي استجابة من الخادم. بمجرد تلقي الاستجابة من الخادم ، يتم التخلص من النتيجة المتفائلة واستبدالها بالنتيجة الفعلية.

لمزيد من المعلومات حول GraphQL من جانب العميل ، خصص ساعة مع المنشئ المشارك لـ GraphQL وأشخاص رائعين آخرين على راديو GraphQL.

ما هو عميل أبولو؟

Apollo Client هو عميل GraphQL قابل للتشغيل البيني ، ومرن للغاية ، ويدفعه المجتمع من أجل JavaScript والأنظمة الأساسية الأصلية. تشمل ميزاته المثيرة للإعجاب أداة قوية لإدارة الحالة (Apollo Link) ، ونظام تخزين مؤقت صفري التكوين ، ونهج تعريفي لجلب البيانات ، وتقسيم الصفحات سهل التنفيذ ، وواجهة مستخدم Optimistic لتطبيقك من جانب العميل.

لا يخزن Apollo Client الحالة من البيانات التي يتم جلبها من الخادم فحسب ، بل يخزن أيضًا الحالة التي أنشأها محليًا على العميل ؛ ومن ثم ، فإنه يدير الحالة لكل من بيانات واجهة برمجة التطبيقات والبيانات المحلية.

من المهم أيضًا ملاحظة أنه يمكنك استخدام Apollo Client جنبًا إلى جنب مع أدوات إدارة الحالة الأخرى ، مثل Redux ، دون تعارض. بالإضافة إلى ذلك ، من الممكن ترحيل إدارة حالتك من ، على سبيل المثال ، Redux إلى عميل Apollo (وهو ما يتجاوز نطاق هذه المقالة). في النهاية ، الغرض الرئيسي من Apollo Client هو تمكين المهندسين من الاستعلام عن البيانات في واجهة برمجة التطبيقات بسلاسة.

ميزات عميل أبولو

لقد فاز عميل Apollo بالعديد من المهندسين والشركات بسبب ميزاته المفيدة للغاية التي تجعل إنشاء تطبيقات قوية حديثة أمرًا سهلاً. تأتي الميزات التالية مخبوزة:

  • التخزين المؤقت
    يدعم عميل Apollo التخزين المؤقت على الطاير.
  • واجهة مستخدم متفائلة
    يتمتع عميل Apollo بدعم رائع لواجهة المستخدم المتفائلة. يتضمن عرض الحالة النهائية للعملية (الطفرة) مؤقتًا أثناء تقدم العملية. بمجرد اكتمال العملية ، تحل البيانات الحقيقية محل البيانات المتفائلة.
  • ترقيم الصفحات
    يحتوي Apollo Client على وظائف مدمجة تجعل من السهل جدًا تنفيذ ترقيم الصفحات في تطبيقك. إنه يعتني بمعظم المشاكل التقنية لجلب قائمة البيانات ، إما في تصحيحات أو دفعة واحدة ، باستخدام وظيفة fetchMore ، التي تأتي مع خطاف useQuery .

في هذه المقالة ، سنلقي نظرة على مجموعة مختارة من هذه الميزات.

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

بناء تطبيق الويب الخاص بنا

هذا المشروع مستوحى من Scott Moss.

سننشئ تطبيق ويب بسيطًا لمتجر الحيوانات الأليفة ، تتضمن ميزاته ما يلي:

  • جلب حيواناتنا الأليفة من جانب الخادم ؛
  • إنشاء حيوان أليف (والذي يتضمن إنشاء الاسم ونوع الحيوان والصورة) ؛
  • باستخدام واجهة المستخدم المتفائلة ؛
  • باستخدام ترقيم الصفحات لتقسيم بياناتنا.

للبدء ، قم باستنساخ المستودع ، مع التأكد من أن فرع starter هو ما قمت باستنساخه.

ابدء

  • قم بتثبيت ملحق Apollo Client Developer Tools لمتصفح Chrome.
  • باستخدام واجهة سطر الأوامر (CLI) ، انتقل إلى دليل المستودع المستنسخ ، وقم بتشغيل الأمر للحصول على جميع التبعيات: npm install .
  • قم بتشغيل الأمر npm run app لبدء التطبيق.
  • أثناء وجودك في المجلد الجذر ، قم بتشغيل الأمر npm run server . سيبدأ هذا الخادم الخلفي الخاص بنا ، والذي سنستخدمه أثناء المضي قدمًا.

يجب أن يفتح التطبيق في منفذ تم تكوينه. المنجم هو https://localhost:1234/ ؛ لك ربما شيء آخر.

إذا كان كل شيء يعمل بشكل جيد ، فيجب أن يبدو تطبيقك كما يلي:

فرع بداية مستنسخ UI
فرع بداية مستنسخ UI. (معاينة كبيرة)

ستلاحظ أنه ليس لدينا حيوانات أليفة لعرضها. هذا لأننا لم ننشئ مثل هذه الوظيفة حتى الآن.

إذا قمت بتثبيت Apollo Client Developer Tools بشكل صحيح ، فافتح أدوات المطور وانقر على أيقونة العلبة. سترى "أبولو" وشيء من هذا القبيل:

أدوات مطوري Apollo Client
أدوات مطوري Apollo Client. (معاينة كبيرة)

مثل أدوات المطورين Redux و React ، سنستخدم Apollo Client Developer Tools لكتابة واختبار استفساراتنا وطفراتنا. يأتي الامتداد مع GraphQL Playground.

جلب الحيوانات الأليفة

دعونا نضيف الوظائف التي تجلب الحيوانات الأليفة. انتقل إلى client/src/client.js . سنقوم بكتابة عميل Apollo ، وربطه بواجهة برمجة التطبيقات ، وتصديره كعميل افتراضي ، وكتابة استعلام جديد.

انسخ الكود التالي والصقه في client.js :

 import { ApolloClient } from 'apollo-client' import { InMemoryCache } from 'apollo-cache-inmemory' import { HttpLink } from 'apollo-link-http' const link = new HttpLink({ uri: 'https://localhost:4000/' }) const cache = new InMemoryCache() const client = new ApolloClient({ link, cache }) export default client

فيما يلي شرح لما يحدث أعلاه:

  • ApolloClient
    ستكون هذه هي الوظيفة التي تغلف تطبيقنا ، وبالتالي واجهات مع HTTP وتخزين البيانات مؤقتًا وتحديث واجهة المستخدم.
  • InMemoryCache
    هذا هو مخزن البيانات العادي في Apollo Client الذي يساعد في معالجة ذاكرة التخزين المؤقت في تطبيقنا.
  • HttpLink
    هذه واجهة شبكة قياسية لتعديل تدفق التحكم لطلبات GraphQL وجلب نتائج GraphQL. تعمل كبرنامج وسيط ، حيث تجلب النتائج من خادم GraphQL في كل مرة يتم فيها تشغيل الارتباط. بالإضافة إلى أنه بديل جيد للخيارات الأخرى ، مثل Axios و window.fetch .
  • نعلن عن متغير ارتباط تم تعيينه لمثيل HttpLink . يأخذ خاصية uri وقيمة لخادمنا ، وهي https://localhost:4000/ .
  • التالي هو متغير ذاكرة تخزين مؤقت يحتفظ بالمثيل الجديد لـ InMemoryCache .
  • يأخذ متغير العميل أيضًا مثيلًا لـ ApolloClient ويلف link cache .
  • أخيرًا ، نقوم بتصدير client حتى نتمكن من استخدامه عبر التطبيق.

قبل أن نتمكن من رؤية هذا أثناء العمل ، يجب أن نتأكد من أن تطبيقنا بأكمله معرض لـ Apollo وأن تطبيقنا يمكنه تلقي البيانات التي يتم جلبها من الخادم وأنه يمكنه تغيير هذه البيانات.

لتحقيق ذلك ، دعنا ننتقل إلى client/src/index.js :

 import React from 'react' import ReactDOM from 'react-dom' import { BrowserRouter } from 'react-router-dom' import { ApolloProvider } from '@apollo/react-hooks' import App from './components/App' import client from './client' import './index.css' const Root = () => ( <BrowserRouter>
 <ApolloProvider client={client}> <App /> </ApolloProvider>
 </BrowserRouter> ); ReactDOM.render(<Root />, document.getElementById('app')) if (module.hot) { module.hot.accept() }

كما ستلاحظ في الكود المميز ، فقد قمنا بلف مكون App في ApolloProvider بتمرير العميل كعنصر خاص إلى client . ApolloProvider مشابه لسياق Context.Provider . إنه يلف تطبيق React الخاص بك ويضع العميل في السياق ، مما يسمح لك بالوصول إليه من أي مكان في شجرة المكونات الخاصة بك.

لجلب حيواناتنا الأليفة من الخادم ، نحتاج إلى كتابة استعلامات تطلب الحقول المحددة التي نريدها. توجه إلى client/src/pages/Pets.js ، وانسخ الكود التالي والصقه فيه:

 import React, {useState} from 'react' import gql from 'graphql-tag' import { useQuery, useMutation } from '@apollo/react-hooks' import PetsList from '../components/PetsList' import NewPetModal from '../components/NewPetModal' import Loader from '../components/Loader'

const GET_PETS = gql` query getPets { pets { id name type img } } `;

export default function Pets () { const [modal, setModal] = useState(false)
 const { loading, error, data } = useQuery(GET_PETS); if (loading) return <Loader />; if (error) return <p>An error occured!</p>;


 const onSubmit = input => { setModal(false) } if (modal) { return <NewPetModal onSubmit={onSubmit} onCancel={() => setModal(false)} /> } return ( <div className="page pets-page"> <section> <div className="row betwee-xs middle-xs"> <div className="col-xs-10"> <h1>Pets</h1> </div> <div className="col-xs-2"> <button onClick={() => setModal(true)}>new pet</button> </div> </div> </section> <section>
 <PetsList pets={data.pets}/>
 </section> </div> ) }

من خلال بضع أجزاء من التعليمات البرمجية ، يمكننا جلب الحيوانات الأليفة من الخادم.

ما هو gql؟

من المهم ملاحظة أن العمليات في GraphQL تكون بشكل عام كائنات JSON مكتوبة graphql-tag و backticks.

علامات gql هي علامات حرفية لقالب جافا سكريبت تحلل سلاسل استعلام GraphQL في GraphQL AST (شجرة بناء الجملة المجردة).

  • عمليات الاستعلام
    من أجل جلب حيواناتنا الأليفة من الخادم ، نحتاج إلى إجراء عملية استعلام.
    • نظرًا لأننا نجري عملية query ، فقد احتجنا إلى تحديد type العملية قبل تسميتها.
    • اسم استعلامنا هو GET_PETS . إنها اصطلاح تسمية لـ GraphQL لاستخدام camelCase لأسماء الحقول.
    • اسم حقولنا pets . ومن ثم ، فإننا نحدد الحقول التي نحتاجها بالضبط من الخادم (id, name, type, img) .
    • useQuery هو خطاف React وهو الأساس لتنفيذ الاستعلامات في تطبيق Apollo. لإجراء عملية استعلام في مكون React الخاص بنا ، نسمي الخطاف useQuery ، والذي تم استيراده في البداية من @apollo/react-hooks . بعد ذلك ، نمرر لها سلسلة استعلام GraphQL ، وهي GET_PETS في حالتنا.
  • عندما يتم عرض المكون الخاص بنا ، تُرجع useQuery استجابة كائن من عميل Apollo تحتوي على خصائص التحميل والخطأ والبيانات. وبالتالي ، يتم تدميرها ، حتى نتمكن من استخدامها لعرض واجهة المستخدم.
  • useQuery رائع. لا يتعين علينا تضمين async-await . لقد تم الاهتمام به بالفعل في الخلفية. رائع ، أليس كذلك؟
    • loading
      تساعدنا هذه الخاصية في التعامل مع حالة تحميل التطبيق. في حالتنا ، نعيد مكون Loader أثناء تحميل تطبيقنا. افتراضيا ، التحميل false .
    • error
      فقط في حالة استخدام هذه الخاصية للتعامل مع أي خطأ قد يحدث.
    • data
      هذا يحتوي على بياناتنا الفعلية من الخادم.
    • أخيرًا ، في مكون PetsList بنا ، نقوم بتمرير دعائم pets ، مع data.pets كقيمة كائن.

في هذه المرحلة ، نجحنا في الاستعلام عن خادمنا.

لبدء تطبيقنا ، لنقم بتشغيل الأمر التالي:

  • ابدأ تشغيل تطبيق العميل. قم بتشغيل الأمر npm run app في CLI الخاص بك.
  • ابدأ الخادم. قم بتشغيل الأمر npm run server في CLI آخر.
تم تقسيم VScode CLI لبدء كل من العميل والخادم.
تم تقسيم VScode CLI لبدء كل من العميل والخادم. (معاينة كبيرة)

إذا سارت الأمور على ما يرام ، يجب أن ترى هذا:

الحيوانات الأليفة استفسرت من الخادم.
الحيوانات الأليفة استفسرت من الخادم.

تحويل البيانات

تغيير البيانات أو إنشاء البيانات في Apollo Client هو نفسه تقريبًا الاستعلام عن البيانات ، مع تغييرات طفيفة جدًا.

لا يزال في client/src/pages/Pets.js ، فلننسخ ولصق الكود المميز:

 .... const GET_PETS = gql` query getPets { pets { id name type img } } `;

const NEW_PETS = gql` mutation CreateAPet($newPet: NewPetInput!) { addPet(input: $newPet) { id name type img } } `;

 const Pets = () => { const [modal, setModal] = useState(false) const { loading, error, data } = useQuery(GET_PETS);
 const [createPet, newPet] = useMutation(NEW_PETS);
 const onSubmit = input => { setModal(false)
 createPet({ variables: { newPet: input } }); } if (loading || newPet.loading) return <Loader />; if (error || newPet.error) return <p>An error occured</p>;
  
 if (modal) { return <NewPetModal onSubmit={onSubmit} onCancel={() => setModal(false)} /> } return ( <div className="page pets-page"> <section> <div className="row betwee-xs middle-xs"> <div className="col-xs-10"> <h1>Pets</h1> </div> <div className="col-xs-2"> <button onClick={() => setModal(true)}>new pet</button> </div> </div> </section> <section> <PetsList pets={data.pets}/> </section> </div> ) } export default Pets

لإنشاء طفرة ، سنتخذ الخطوات التالية.

1. mutation

للإنشاء أو التحديث أو الحذف ، نحتاج إلى إجراء عملية mutation . عملية mutation لها اسم CreateAPet ، مع وسيطة واحدة. تحتوي هذه الوسيطة على متغير $newPet ، مع نوع NewPetInput . ال ! يعني أن العملية مطلوبة ؛ وبالتالي ، لن تنفذ GraphQL العملية إلا إذا newPet متغيرًا جديدًا من نوع بيت نوع NewPetInput .

2. addPet

تأخذ وظيفة addPet ، الموجودة داخل عملية mutation ، وسيطة input ويتم تعيينها على متغير $newPet . يجب أن تكون مجموعات الحقول المحددة في وظيفة addPet بنا مساوية لمجموعات الحقول في استعلامنا. المجموعات الميدانية في عمليتنا هي:

  • id
  • name
  • type
  • img

3. useMutation

خطاف رد الفعل useMutation هو واجهة برمجة التطبيقات الأساسية لتنفيذ الطفرات في تطبيق Apollo. عندما نحتاج إلى تغيير البيانات ، فإننا نسمي useMutation في مكون React ونمررها سلسلة GraphQL (في حالتنا ، NEW_PETS ).

عندما يعرض المكون الخاص بنا useMutation ، فإنه يعيد مجموعة (أي ، مجموعة مرتبة من البيانات تشكل سجلا) في مصفوفة تتضمن:

  • دالة mutate يمكننا استدعاؤها في أي وقت لتنفيذ الطفرة ؛
  • كائن مع الحقول التي تمثل الوضع الحالي لتنفيذ الطفرة.

يتم تمرير خطاف useMutation سلسلة طفرة GraphQL (وهي NEW_PETS في حالتنا). لقد دمرنا المجموعة ، وهي الوظيفة ( createPet ) التي ستغير البيانات وحقل الكائن ( newPets ).

4. createPet

في دالة onSubmit الخاصة بنا ، بعد فترة وجيزة من حالة setModal ، حددنا createPet . تأخذ هذه الوظيفة variable بخاصية كائن لقيمة معينة إلى { newPet: input } . يمثل input حقول الإدخال المختلفة في نموذجنا (مثل الاسم والنوع وما إلى ذلك).

بعد ذلك ، يجب أن تبدو النتيجة كما يلي:

طفرة بدون تحديث فوري
طفرة بدون تحديث فوري.

إذا لاحظت صورة GIF عن كثب ، فستلاحظ أن حيواننا الأليف الذي تم إنشاؤه لا يظهر على الفور ، فقط عندما يتم تحديث الصفحة. ومع ذلك ، فقد تم تحديثه على الخادم.

السؤال الكبير هو ، لماذا لا يتم تحديث حيواننا الأليف على الفور؟ دعنا نكتشف في القسم التالي.

التخزين المؤقت في عميل أبولو

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

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

حفظ ذاكرة التخزين المؤقت متزامنة

هناك عدة طرق للحفاظ على مزامنة ذاكرة التخزين المؤقت في كل مرة نقوم فيها بإجراء عملية طفرة.

الأول هو إعادة جلب استعلامات المطابقة بعد حدوث طفرة ، باستخدام خاصية الكائن refetchQueries (أبسط طريقة).

ملاحظة: إذا أردنا استخدام هذه الطريقة ، فستأخذ خاصية الكائن في دالة createPet تسمى refetchQueries ، وستحتوي على مصفوفة من الكائنات ذات قيمة الاستعلام: refetchQueries: [{ query: GET_PETS }] .

نظرًا لأن تركيزنا في هذا القسم ليس فقط تحديث الحيوانات الأليفة التي أنشأناها في واجهة المستخدم ، ولكن لمعالجة ذاكرة التخزين المؤقت ، فلن نستخدم هذه الطريقة.

الطريقة الثانية هي استخدام وظيفة update . في Apollo Client ، توجد وظيفة مساعد update التي تساعد على تعديل بيانات ذاكرة التخزين المؤقت ، بحيث تتزامن مع التعديلات التي تجريها الطفرة على بياناتنا الخلفية. باستخدام هذه الوظيفة ، يمكننا القراءة والكتابة في ذاكرة التخزين المؤقت.

تحديث ذاكرة التخزين المؤقت

انسخ الكود المميز التالي والصقه في client/src/pages/Pets.js :

 ...... const Pets = () => { const [modal, setModal] = useState(false) const { loading, error, data } = useQuery(GET_PETS);
 const [createPet, newPet] = useMutation(NEW_PETS, { update(cache, { data: { addPet } }) { const data = cache.readQuery({ query: GET_PETS }); cache.writeQuery({ query: GET_PETS, data: { pets: [addPet, ...data.pets] }, }); }, } );
 .....

تتلقى وظيفة update وسيطين:

  • الوسيطة الأولى هي ذاكرة التخزين المؤقت من عميل Apollo.
  • والثاني هو استجابة الطفرة الدقيقة من الخادم. ندمر خاصية data ونضبطها على الطفرة ( addPet ).

بعد ذلك ، لتحديث الوظيفة ، نحتاج إلى التحقق من الاستعلام الذي يجب تحديثه (في حالتنا ، استعلام GET_PETS ) وقراءة ذاكرة التخزين المؤقت.

ثانيًا ، نحتاج إلى الكتابة إلى query الذي تمت قراءته ، حتى يعرف أننا على وشك تحديثه. نقوم بذلك عن طريق تمرير كائن يحتوي على خاصية كائن query ، مع تعيين القيمة لعملية query ( GET_PETS ) ، وخاصية data تكون قيمتها كائنًا pet أليفًا وتحتوي على مصفوفة من طفرة addPet ونسخة من بيانات الحيوانات الأليفة.

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

يتم تحديث الحيوانات الأليفة على الفور
يتم تحديث الحيوانات الأليفة على الفور.

واجهة مستخدم متفائلة

كثير من الناس هم من كبار المعجبين باللوادر والغزل. لا حرج في استخدام اللودر ؛ هناك حالات استخدام مثالية يكون فيها اللودر هو الخيار الأفضل. لقد كتبت عن اللوادر مقابل الغزالات وأفضل حالات استخدامها.

تلعب اللوادر والعجلات دورًا مهمًا في تصميم واجهة المستخدم وتجربة المستخدم ، لكن وصول واجهة المستخدم المتفائلة قد سرق الأضواء.

ما هي واجهة المستخدم المتفائلة؟

Optimistic UI هي اتفاقية تحاكي نتائج الطفرة (البيانات التي تم إنشاؤها) وتقوم بتحديث واجهة المستخدم قبل تلقي استجابة من الخادم. بمجرد تلقي الاستجابة من الخادم ، يتم التخلص من النتيجة المتفائلة واستبدالها بالنتيجة الفعلية.

في النهاية ، لا تعد واجهة المستخدم المتفائلة أكثر من طريقة لإدارة الأداء المتصور وتجنب حالات التحميل.

لدى Apollo Client طريقة شيقة جدًا لدمج واجهة المستخدم المتفائلة. يعطينا خطافًا بسيطًا يسمح لنا بالكتابة إلى ذاكرة التخزين المؤقت المحلية بعد حدوث طفرة. دعونا نرى كيف يعمل!

الخطوة 1

توجه إلى client/src/client.js ، وأضف الكود المميز فقط.

 import { ApolloClient } from 'apollo-client' import { InMemoryCache } from 'apollo-cache-inmemory' import { HttpLink } from 'apollo-link-http'
import { setContext } from 'apollo-link-context' import { ApolloLink } from 'apollo-link' const http = new HttpLink({ uri: "https://localhost:4000/" }); const delay = setContext( request => new Promise((success, fail) => { setTimeout(() => { success() }, 800) }) ) const link = ApolloLink.from([ delay, http ])
const cache = new InMemoryCache() const client = new ApolloClient({ link, cache }) export default client

تتضمن الخطوة الأولى ما يلي:

  • نقوم باستيراد setContext من apollo-link-context . تأخذ وظيفة setContext وظيفة رد الاتصال وتعيد الوعد الذي تم تعيينه setTimeout على 800ms ، من أجل إنشاء تأخير عند تنفيذ عملية الطفرة.
  • تضمن طريقة ApolloLink.from نشاط الشبكة الذي يمثل الرابط (API الخاص بنا) من HTTP .

الخطوة 2

الخطوة التالية هي استخدام خطاف Optimistic UI. مرر مرة أخرى إلى client/src/pages/Pets.js ، وأضف الرمز المميز أدناه فقط.

 ..... const Pets = () => { const [modal, setModal] = useState(false) const { loading, error, data } = useQuery(GET_PETS); const [createPet, newPet] = useMutation(NEW_PETS, { update(cache, { data: { addPet } }) { const data = cache.readQuery({ query: GET_PETS }); cache.writeQuery({ query: GET_PETS, data: { pets: [addPet, ...data.pets] }, }); }, } ); const onSubmit = input => { setModal(false) createPet({ variables: { newPet: input },
 optimisticResponse: { __typename: 'Mutation', addPet: { __typename: 'Pet', id: Math.floor(Math.random() * 10000 + ''), name: input.name, type: input.type, img: 'https://via.placeholder.com/200' } }
 }); } .....

يتم استخدام كائن optimisticResponse إذا أردنا تحديث واجهة المستخدم على الفور عند إنشاء حيوان أليف ، بدلاً من انتظار استجابة الخادم.

تتضمن مقتطفات التعليمات البرمجية أعلاه ما يلي:

  • __typename يتم إدخاله بواسطة Apollo في الاستعلام لجلب type الكيانات التي تم الاستعلام عنها. يتم استخدام هذه الأنواع بواسطة Apollo Client لبناء خاصية id (وهي رمز) لأغراض التخزين المؤقت في apollo-cache . لذا ، فإن __typename خاصية صالحة لاستجابة الاستعلام.
  • يتم تعيين الطفرة على أنها __typename اسم نوع الاستجابة optimisticResponse .
  • تمامًا كما تم تحديده سابقًا ، اسم الطفرة هو addPet ، و __typename هو حيوان Pet .
  • فيما يلي مجالات الطفرة التي نريد تحديث الاستجابة المتفائلة:
    • id
      نظرًا لأننا لا نعرف ما هو المعرف من الخادم ، فقد أنشأنا واحدًا باستخدام Math.floor .
    • name
      تم تعيين هذه القيمة على input.name .
    • type
      قيمة النوع هي input.type .
    • img
      الآن ، نظرًا لأن خادمنا يقوم بإنشاء صور لنا ، فقد استخدمنا عنصرًا نائبًا لتقليد صورتنا من الخادم.

كانت هذه بالفعل رحلة طويلة. إذا وصلت إلى النهاية ، فلا تتردد في أخذ قسط من الراحة من مقعدك مع فنجان من القهوة.

دعونا نلقي نظرة على نتائجنا. المستودع الداعم لهذا المشروع موجود على GitHub. استنساخ وجربها.

النتيجة النهائية لتطبيق متجر الحيوانات الأليفة
النتيجة النهائية لتطبيقنا.

خاتمة

الميزات المذهلة لـ Apollo Client ، مثل Optimistic UI و pagination ، تجعل بناء التطبيقات من جانب العميل حقيقة واقعة.

بينما يعمل Apollo Client بشكل جيد للغاية مع أطر أخرى ، مثل Vue.js و Angular ، فإن مطوري React لديهم Apollo Client Hooks ، وبالتالي لا يمكنهم إلا الاستمتاع ببناء تطبيق رائع.

في هذه المقالة ، خدشنا السطح فقط. يتطلب إتقان عميل Apollo ممارسة مستمرة. لذا ، امض قدمًا وقم باستنساخ المستودع وإضافة ترقيم الصفحات واللعب بالميزات الأخرى التي يوفرها.

يرجى مشاركة ملاحظاتك وتجربتك في قسم التعليقات أدناه. يمكننا أيضًا مناقشة تقدمك على Twitter. هتافات!

مراجع

  • "برنامج GraphQL في رد الفعل من جانب العميل" ، سكوت موس ، رئيس الواجهة الأمامية
  • "التوثيق" ، عميل أبولو
  • "واجهة المستخدم المتفائلة مع التفاعل" ، باتريك أندريهجفسكي
  • "الأكاذيب الحقيقية لواجهات المستخدم المتفائلة" ، مجلة Smashing