التسوية التلقائية وتحديثات البيانات لمكتبات جلب البيانات (react-query، swr، rtk-query والمزيد)
مقدمة
تحفيز
تثبيت
الشروط المطلوبة
تطبيع المصفوفات
تصحيح الأخطاء
أداء
التكامل
أمثلة
normy
هي مكتبة تسمح بتسوية بيانات تطبيقك تلقائيًا. وبعد ذلك، بمجرد تسوية البيانات، يمكن في كثير من الحالات تحديث بياناتك تلقائيًا.
جوهر normy
- وهي مكتبة @normy/core
، والتي لا يُقصد استخدامها مباشرة في التطبيقات، لديها منطق بداخلها يسمح بالتكامل بسهولة مع مكتبات جلب البيانات المفضلة لديك. توجد بالفعل عمليات تكامل رسمية مع react-query
و swr
و RTK Query
. إذا كنت تستخدم مكتبة جلب أخرى، فيمكنك إثارة مشكلة Github، لذلك قد تتم إضافتها أيضًا.
من أجل فهم ما يفعله normy
في الواقع، فمن الأفضل أن نرى مثالا. لنفترض أنك تستخدم react-query
. ثم يمكنك إعادة بناء الكود بالطريقة التالية:
استيراد رد فعل من "رد فعل"؛ يستورد { كويريكلينتبروفيدر، عميل الاستعلام, استخدامQueryClient, } from '@tanstack/react-query';+ import { QueryNormalizerProvider } from '@normy/react-query'; const queryClient = new QueryClient(); كتب ثابتة = () => { const queryClient = useQueryClient(); const { data: booksData = [] } = useQuery(['books'], () => الوعد.الحل([ { المعرف: '1'، الاسم: 'الاسم 1'، المؤلف: { المعرف: '1001'، الاسم: 'المستخدم1' } }، { المعرف: '2'، الاسم: 'الاسم 2'، المؤلف: { المعرف: '1002'، الاسم: 'المستخدم2' } }، ])، ); const { data: bookData } = useQuery(['book'], () => الوعد.الحل({ المعرف: '1'، الاسم: "الاسم 1"، المؤلف: {المعرف: '1001'، الاسم: 'المستخدم1' }، }), ); const updateBookNameMutation = useMutation({ طفرة فن: () => ({ المعرف: '1'، الاسم: "تم تحديث الاسم 1"، }),- onSuccess: MutationData => {- queryClient.setQueryData(['books'], data =>- data.map(book =>- book.id ===mutarData.id ? { ...book, . ..mutationData } : كتاب,- ),- );- queryClient.setQueryData(['book'], data =>- data.id === MutationData.id ? { ...data, ...mutationData } : data,- );- },}); const updateBookAuthorMutation = useMutation({ طفرة فن: () => ({ المعرف: '1'، المؤلف: {المعرف: '1004'، الاسم: 'User4' }، }),- onSuccess: MutationData => {- queryClient.setQueryData(['books'], data =>- data.map(book =>- book.id ===mutarData.id ? { ...book, . ..mutationData } : كتاب,- ),- );- queryClient.setQueryData(['book'], data =>- data.id === MutationData.id ? { ...data, ...mutationData } : data,- );- },}); const addBookMutation = useMutation({ طفرة فن: () => ({ المعرف: '3'، الاسم: "الاسم 3"، المؤلف: {المعرف: '1003'، الاسم: 'User3' }، }), // مع البيانات ذات المصفوفات ذات المستوى الأعلى، لا تزال بحاجة إلى تحديث البيانات يدويًا onSuccess: MutationData => { queryClient.setQueryData(['books'], data => data.concat(mutationData)); }, }); // إرجاع بعض JSX }; const App = () => (+ <QueryNormalizerProvider queryClient={queryClient}> <QueryClientProvider client={queryClient}> <الكتب /> </QueryClientProvider>+ </QueryNormalizerProvider> );
لذلك، كما ترون، وبصرف النظر عن صفائف المستوى الأعلى، لم تعد هناك حاجة إلى تحديثات يدوية للبيانات. يعد هذا مفيدًا بشكل خاص إذا كان يجب على طفرة معينة تحديث البيانات لاستعلامات متعددة. ليس هذا فقط مطولًا لإجراء التحديثات يدويًا، ولكن عليك أيضًا أن تعرف بالضبط الاستعلامات التي تريد تحديثها. كلما زاد عدد الاستفسارات لديك، زادت المزايا التي يجلبها normy
.
كيف يعمل؟ افتراضيًا، يتم تنظيم جميع الكائنات التي تحتوي على مفتاح id
حسب معرفاتها. الآن، سيتم تسوية أي كائن id
مفتاح، وهو ما يعني ببساطة تخزينه بواسطة المعرف. إذا كان هناك بالفعل كائن مطابق له نفس المعرف، فسيتم دمج كائن جديد بعمق مع الكائن الموجود بالفعل في الحالة. لذلك، إذا كانت بيانات استجابة الخادم من الطفرة هي { id: '1', title: 'new title' }
، فستكتشف هذه المكتبة تلقائيًا تحديث title
الكائن id: '1'
لجميع الاستعلامات التابعة.
كما أنه يعمل مع الكائنات المتداخلة ذات المعرفات، بغض النظر عن مدى عمقها. إذا كان الكائن ذو المعرف يحتوي على كائنات أخرى ذات معرفات، فسيتم تطبيعها بشكل منفصل وسيكون للكائن الأصلي مرجع فقط لتلك الكائنات المتداخلة.
لتثبيت الحزمة، فقط قم بتشغيل:
$ npm install @normy/react-query
أو يمكنك فقط استخدام CDN: https://unpkg.com/@normy/react-query
.
لتثبيت الحزمة، فقط قم بتشغيل:
$ npm install @normy/swr
أو يمكنك فقط استخدام CDN: https://unpkg.com/@normy/swr
.
لتثبيت الحزمة، فقط قم بتشغيل:
$ npm install @normy/rtk-query
أو يمكنك فقط استخدام CDN: https://unpkg.com/@normy/rtk-query
.
إذا كنت تريد كتابة مكون إضافي لمكتبة أخرى غير react-query
أو swr
أو rtk-query
:
$ npm install @normy/core
أو يمكنك فقط استخدام CDN: https://unpkg.com/@normy/core
.
لمعرفة كيفية كتابة مكون إضافي، ما عليك سوى التحقق الآن من الكود المصدري لـ @normy/react-query
، فمن السهل جدًا القيام بذلك، وسيتم إنشاء دليل في المستقبل.
لكي تعمل التسوية التلقائية، يجب استيفاء الشروط التالية:
يجب أن يكون لديك طريقة موحدة لتحديد الكائنات الخاصة بك، ويتم ذلك عادة عن طريق id
المفتاح
يجب أن تكون المعرفات فريدة عبر التطبيق بأكمله، وليس فقط عبر أنواع الكائنات، وإذا لم يكن الأمر كذلك، فستحتاج إلى إلحاق شيء ما بها، ويجب القيام بنفس الشيء في عالم GraphQL، وعادةً ما يتم إضافة _typename
يجب أن يكون للكائنات التي لها نفس المعرفات بنية متسقة، إذا كان كائن مثل كتاب في أحد الاستعلامات يحتوي على مفتاح title
، فيجب أن يكون title
في استعلام آخر، وليس name
مفاجئًا
هناك وظيفة يمكن تمريرها إلى createQueryNormalizer
لتلبية تلك المتطلبات، وهي getNormalizationObjectKey
.
يمكن أن يساعدك getNormalizationObjectKey
في النقطة الأولى، على سبيل المثال، إذا حددت الكائنات بشكل مختلف، مثل مفتاح _id
، فيمكنك تمرير getNormalizationObjectKey: obj => obj._id
.
يتيح لك getNormalizationObjectKey
أيضًا اجتياز المطلب الثاني. على سبيل المثال، إذا كانت معرفاتك فريدة، ولكن ليس عبر التطبيق بأكمله، ولكن ضمن أنواع الكائنات، فيمكنك استخدام getNormalizationObjectKey: obj => obj.id && obj.type ? obj.id + obj.type : undefined
أو شيء مشابه. إذا لم يكن ذلك ممكنًا، فيمكنك فقط حساب اللاحقة بنفسك، على سبيل المثال:
const getType = obj => { إذا (obj.bookTitle) {return 'book'؛ } إذا (obj.surname) {return 'user'؛ } إرجاع غير محدد؛}؛createQueryNormalizer(queryClient, { getNormalizationObjectKey: obj =>obj.id && getType(obj) && obj.id + getType(obj),});
يجب دائمًا تلبية النقطة 3، وإذا لم يكن الأمر كذلك، فيجب عليك أن تطلب من مطوري الواجهة الخلفية لديك الحفاظ على الأمور موحدة ومتسقة. وكحل أخير، يمكنك تعديل الردود من جانبك.
لسوء الحظ، هذا لا يعني أنك لن تحتاج أبدًا إلى تحديث البيانات يدويًا بعد الآن. لا تزال هناك حاجة إلى إجراء بعض التحديثات يدويًا كما هو معتاد، أي إضافة العناصر وإزالتها من المصفوفة. لماذا؟ تخيل طفرة REMOVE_BOOK
. قد يكون هذا الكتاب موجودًا في العديد من الاستعلامات، ولا تستطيع المكتبة معرفة الاستعلامات التي ترغب في إزالته منها. الأمر نفسه ينطبق على ADD_BOOK
، فلا يمكن للمكتبة معرفة الاستعلام الذي يجب إضافة الكتاب إليه، أو حتى فهرس المصفوفة. نفس الشيء بالنسبة لإجراء مثل SORT_BOOKS
. تؤثر هذه المشكلة على صفائف المستوى الأعلى فقط. على سبيل المثال، إذا كان لديك كتاب بمعرف ما ومفتاح آخر مثل likedByUsers
، فإذا قمت بإرجاع كتاب جديد بقائمة محدثة في likedByUsers
، فسيعمل هذا مرة أخرى تلقائيًا.
ومع ذلك، في الإصدار المستقبلي من المكتبة، مع بعض المؤشرات الإضافية، سيكون من الممكن إجراء التحديثات المذكورة أعلاه أيضًا!
إذا كنت مهتمًا، فما الذي تفعله normy
معالجة البيانات في الواقع، يمكنك استخدام خيار devLogging
:
<QueryNormalizerProvider استعلام عميل = {queryClient} NormalizerConfig={{ devLogging: true }}> {الأطفال</QueryNormalizerProvider>
false
بشكل افتراضي، إذا تم تعيينه على true
، يمكنك أن ترى في معلومات وحدة التحكم، عندما يتم تعيين الاستعلامات أو إزالتها.
لاحظ أن هذا يعمل فقط في التطوير، حتى لو قمت بتمرير true
، فلن يتم إجراء أي تسجيل في الإنتاج (عندما يكون ذلك على وجه التحديد هو process.env.NODE_ENV === 'production'
). عادةً ما يتم تعيين NODE_ENV
بواسطة مجمعات الوحدات مثل webpack
، لذا ربما لا داعي للقلق بشأن تعيين NODE_ENV
بنفسك.
وكما هو الحال دائمًا، فإن أي عملية أتمتة لها تكلفة. يمكن إضافة بعض المعايير في المستقبل، ولكن في الوقت الحالي أظهرت الاختبارات اليدوية أنه ما لم يكن لديك في بياناتك عشرات الآلاف من الكائنات التي تمت تسويتها، فلن يكون الحمل الزائد ملحوظًا. ومع ذلك، لديك عدة طرق مرنة لتحسين الأداء:
يمكنك فقط تسوية الاستعلامات التي تحتوي على تحديثات للبيانات، والطفرات التي يجب أن تقوم بتحديث البيانات فقط - هذا كل شيء، يمكنك تسوية جزء فقط من بياناتك. تحقق من وثائق التكامل حول كيفية القيام بذلك.
مثل 1.
ولكن للاستعلامات والطفرات ذات البيانات الكبيرة للغاية.
هناك تحسين مضمن، والذي يتحقق من البيانات من استجابات الطفرات إذا كانت مختلفة بالفعل عن البيانات الموجودة في المتجر الطبيعي. إذا كان هو نفسه، لن يتم تحديث الاستعلامات التابعة. لذلك، من الجيد أن تتضمن بيانات الطفرات فقط الأشياء التي يمكن أن تكون مختلفة بالفعل، مما قد يمنع تحديثات التسوية والاستعلامات غير الضرورية.
لا تقم بتعطيل خيار structuralSharing
في المكتبات التي تدعمه - إذا كانت بيانات الاستعلام بعد التحديث هي نفسها مرجعيًا كما كانت قبل التحديث، فلن تتم تسوية هذا الاستعلام. يعد هذا تحسينًا كبيرًا للأداء، خاصة بعد الإرجاع عند إعادة التركيز، والذي قد يؤدي إلى تحديث استعلامات متعددة في نفس الوقت، عادةً لنفس البيانات.
يمكنك استخدام الدالة getNormalizationObjectKey
لتعيين الكائنات التي يجب تطبيعها فعليًا على مستوى العالم. على سبيل المثال:
<QueryNormalizerProvider استعلام عميل = {queryClient} NormalizerConfig={{getNormalizationObjectKey: obj => (obj.normalizable ? obj.id : غير محدد)، }}> {الأطفال</QueryNormalizerProvider>
علاوة على ذلك، سيتم إضافة بعض الخيارات الإضافية الخاصة بالأداء في المستقبل.
توجد حاليًا ثلاث عمليات تكامل رسمية مع مكتبات جلب البيانات، وهي: react-query
، swr
، و rtk-query
. راجع الوثائق المخصصة لعمليات التكامل المحددة:
استعلام رد الفعل
swr
استعلام rtk
أوصي بشدة بتجربة أمثلة حول كيفية استخدام هذه الحزمة في التطبيقات الحقيقية.
توجد الأمثلة التالية حاليًا:
استعلام رد الفعل
trpc
swr
استعلام rtk
معهد ماساتشوستس للتكنولوجيا