useMemo: ما هو، تخزين الحسابات مؤقتًا و React Native

المؤلف: IT Sectr نُشر: 2026-07-05 وقت القراءة: 10 دق

يقوم خطاف useMemo في React Native بتخزين نتيجة الحسابات بين عمليات إعادة عرض المكون، مما يمنع التنفيذ المتكرر للعمليات المكلفة. على عكس useCallback الذي يخزّن الدالة، يحتفظ useMemo بالقيمة المعادة ويعيد حسابها فقط عند تغير التبعيات المحددة. وفقًا لـ React Docs, 2024، يكون التخزين المؤقت فعالاً بشكل خاص للحسابات ذات المصفوفات الكبيرة وتحويلات البيانات المعقدة. في React Native، كل إعادة عرض غير ضرورية تُنشئ حملًا إضافيًا على اتصال الجسر بين JavaScript والمسارات الأصلية، لذا فإن الاستخدام الصحيح لـ useMemo يؤثر مباشرة على سلاسة الرسوم المتحركة وسرعة استجابة الواجهة.

النقاط الرئيسية

  • useMemo — خطاف React لتخزين القيم مؤقتًا، يخبئ نتيجة دالة بين عمليات إعادة العرض
  • يقبل الخطاف وسيطين: دالة حسابية ومصفوفة تبعيات
  • يتم إعادة حساب القيمة فقط عندما يتغير تبعية واحدة على الأقل
  • في React Native، useMemo ضروري لتحسين أداء القوائم والرسوم المتحركة
  • الإفراط في استخدام useMemo قد يُضعف الأداء بسبب تكلفة مقارنة التبعيات

ما هو useMemo في React Native

useMemo — هو خطاف من المكتبة الأساسية لـ React، متاح في React Native بدون حزم إضافية. يخزّن القيمة المعادة من الدالة المُمررة ويعيد استخدام هذه القيمة في عمليات إعادة العرض اللاحقة حتى تتغير التبعيات المحددة في المصفوفة.

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

في سياق React Native، التخزين المؤقت مهم بشكل خاص بسبب الخصائص المعمارية. كل إعادة عرض للمكون ترسل بيانات عبر جسر JavaScript — الأصلي، مما يستغرق وقتًا. إذا كان المكون يحتوي على حسابات مكلفة (تصفية قائمة، فرز، تنسيق)، فإن كل إعادة عرض ستكررها، مما يعوق مسار JavaScript ويسبب انخفاض الإطارات.

وفقًا لـ React Native Performance Docs, 2024، غالبًا ما تكون عنق الزجاجة ليست في عرض المكونات الأصلية، بل في تنفيذ منطق JavaScript بين عمليات إعادة العرض. useMemo يحل هذه المشكلة تحديدًا.

كيف يعمل تخزين القيم مؤقتًا

useMemo يستقبل دالة ومصفوفة تبعيات، ويعيد قيمة مخزنة مؤقتًا. يخزن React القيمة السابقة ومصفوفة التبعيات. في كل إعادة عرض، يقارن React التبعيات الحالية مع السابقة باستخدام Object.is. إذا تغير تبعية واحدة على الأقل، يتم تنفيذ الدالة مرة أخرى ويُخزَّن الناتج.

الصياغة وتمرير الوسائط

توقيع useMemo متطابق على الويب وفي React Native. الوسيط الأول هو دالة بدون معلمات تُرجع القيمة المراد تخزينها. الوسيط الثاني هو مصفوفة تبعيات، عند تغيرها سيتم إعادة حساب القيمة.

js
import React, { useMemo } from 'react';

const sortedList = useMemo(() => {
    return items.sort((a, b) => a.name.localeCompare(b.name));
}, [items]);

في هذا المثال، يتم إعادة حساب sortedList فقط عندما يتغير المرجع إلى مصفوفة items. إذا تغير محتوى المصفوفة لكن المرجع بقي كما هو (تحوير)، فلن يكتشف useMemo التغييرات — وهذا قيد مهم.

مقارنة التبعيات عبر Object.is

يستخدم React خوارزمية Object.is لمقارنة التبعيات، والتي تعمل بشكل مشابه للمساواة الصارمة === لكنها تتعامل مع NaN بشكل صحيح (+0 و -0 يُعتبران مختلفين). هذا يعني أنه للأنواع البدائية (سلاسل نصية، أرقام، قيم منطقية)، تعمل المقارنة حسب القيمة، بينما للكائنات والمصفوفات — حسب المرجع.

js
// Object.is({'a': 1}, {'a': 1}) -> false (different refs)
// Object.is(42, 42) -> true (primitive by value)
// Object.is(NaN, NaN) -> true (correct handling of NaN)

فهم هذه الآلية أمر بالغ الأهمية: إذا قمت بتمرير كائن جديد في التبعيات في كل إعادة عرض، سيكون التخزين المؤقت عديم الفائدة لأن التبعيات ستُعتبر متغيرة في كل مرة.

متى تستخدم useMemo

useMemo مبرر في ثلاثة سيناريوهات: الحسابات المكلفة، تمرير props ثابتة للمكونات الفرعية، والحفاظ على الهوية المرجعية للكائنات. في React Native، تحدث السيناريوهات الثلاثة بانتظام بسبب بنية التطبيقات المحمولة.

حسابات مكلفة

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

Props ثابتة للمكونات الفرعية

عندما يكون المكون الفرعي مغلفًا بـ React.memo، فإنه يعيد العرض فقط عندما تتغير props الخاصة به. إذا تم إنشاء كائن prop داخل المكون الأب بدون useMemo، فسيتم إنشاء مرجع جديد في كل إعادة عرض للأب، وسيعيد المكون الفرعي العرض بشكل غير ضروري، مما يلغي فائدة React.memo.

تخزين حسابات الأنماط

في React Native، تعتمد الأنماط غالبًا على props — على سبيل المثال، عرض عنصر يُحسب بناءً على حجم الشاشة. يسمح useMemo بحساب هذه الأنماط الديناميكية فقط عند تغير معلمات الإدخال.

متى يكون useMemo عديم الفائدة

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

الخطاف لا يوفر فائدة في ثلاث حالات رئيسية. أولاً، إذا كان الحساب تافهًا (جمع بسيط، دمج سلاسل نصية)، فإن تكلفة useMemo تتجاوز تكلفة الحساب نفسه. ثانيًا، إذا كانت التبعيات تتغير في كل إعادة عرض — سيتم تنفيذ الدالة على أي حال، والتخزين المؤقت يضيف فقط عملاً إضافيًا. ثالثًا، إذا كان المكون يُعرض نادرًا، فإن الحمل الإضافي لتخزين ذاكرة التخزين المؤقت غير مبرر.

وفقًا لـ Kent C. Dodds, 2023، قبل إضافة useMemo، يجب قياس المشكلة باستخدام React DevTools Profiler أو مراقب الأداء المدمج في React Native. إذا كانت إعادة عرض المكون تستغرق أقل من 1 مللي ثانية، فإن التخزين المؤقت غير ضروري.

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

useMemo و useCallback: مقارنة

useMemo و useCallback — خطافا تخزين مؤقت من React غالبًا ما يتم الخلط بينهما. الفرق يكمن في ما يحتفظان به: useMemo يُرجع نتيجة تنفيذ دالة (أي قيمة)، بينما useCallback يُرجع الدالة نفسها. كلاهما يقبل مصفوفة تبعيات، لكن الصياغة تختلف.

الخاصيةuseMemouseCallback
يُرجعنتيجة تنفيذ الدالةمرجعًا للدالة
الوسيط الأولدالة تُرجع قيمةدالة ليتم تخزينها
الاستخدام النموذجيتخزين الحسابات مؤقتًااستدعاءات ثابتة للمكونات الفرعية
التكافؤuseMemo(() => fn, deps)useCallback(fn, deps) === useMemo(() => fn, deps)

عمليًا، useCallback هو سكر نحوي فوق useMemo للحالات التي تحتاج فيها إلى تخزين دالة. في React Native، يتم استخدام كلا الخطافين بنفس التواتر: useMemo — للبيانات (نص منسق، أنماط محسوبة)، useCallback — لمعالجات الأحداث (onPress, onChangeText).

أمثلة عملية مع useMemo

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

مثال 1: البحث وتصفية القائمة

في هذا المثال، يمنع useMemo التصفية المتكررة لقائمة كبيرة من جهات الاتصال في كل إعادة عرض إذا لم يتغير لا استعلام البحث ولا البيانات المصدر.

js
const ContactList = ({ contacts, searchQuery }) => {
    const filteredContacts = useMemo(() => {
        if (!searchQuery.trim()) return contacts;
        const query = searchQuery.toLowerCase();
        return contacts.filter((c) =>
            c.name.toLowerCase().includes(query) ||
            c.phone.includes(query)
        );
    }, [contacts, searchQuery]);

    return (
        <FlatList
            data={filteredContacts}
            renderItem={renderContact}
            keyExtractor={(item) => item.id}
        />
    );
};

بدون useMemo، كانت التصفية ستُنفذ في كل إعادة عرض، بما في ذلك تلك الناتجة عن الرسوم المتحركة أو تغييرات الحالة في أجزاء أخرى من المكون. مع useMemo، تعمل التصفية فقط عندما تتغير contacts أو searchQuery.

مثال 2: حساب الأنماط الديناميكية

في React Native، تعتمد الأنماط غالبًا على أبعاد الشاشة أو props المكون. يسمح useMemo بحساب كائن الأنماط هذا مرة واحدة وإعادة استخدامه حتى تتغير التبعيات.

js
const Card = ({ title, isSelected }) => {
    const cardStyle = useMemo(() => ({
        backgroundColor: isSelected ? '#e3f2fd' : '#ffffff',
        borderWidth: isSelected ? 2 : 1,
        padding: 16,
    }), [isSelected]);

    return (
        <View style={cardStyle}>
            <Text>{title}</Text>
        </View>
    );
};

يضمن هذا النهج أن كائن cardStyle يبقى مستقرًا (نفس المرجع) حتى يتغير isSelected. إذا كان Card مغلفًا بـ React.memo، يمنع هذا النمط إعادة العرض غير الضرورية للعناصر الفرعية.

الأسئلة الشائعة

ماذا يُرجع useMemo؟

useMemo يُرجع قيمة مخزنة مؤقتًا — نتيجة تنفيذ الدالة المُمررة كوسيط أول. في عمليات إعادة العرض اللاحقة، يُرجع React القيمة المخبأة إذا لم تتغير التبعيات.

كيف يختلف useMemo عن useCallback؟

useMemo يُرجع نتيجة تنفيذ الدالة (أي قيمة: رقم، كائن، JSX)، بينما useCallback يُرجع الدالة نفسها. لاحظ أن useCallback(fn, deps) يعادل useMemo(() => fn, deps).

متى لا نحتاج useMemo؟

useMemo عديم الفائدة في الحسابات التافهة (الجمع، الدمج)، إعادة العرض النادرة، التبعيات التي تتغير في كل إعادة عرض، والأنواع البدائية. قس الأداء دائمًا قبل إضافة useMemo.

هل يمكن أن يُضعف useMemo الأداء؟

نعم، إذا استُخدم بدون داعٍ. يخزن React القيم السابقة في الذاكرة ويقارن التبعيات في كل إعادة عرض. للحسابات البسيطة، يتجاوز الحمل الإضافي لهذه العمليات فائدة التخزين المؤقت.

كيف تتحقق من عمل useMemo؟

استخدم React DevTools Profiler لقياس أوقات عرض المكونات. في React Native، يتوفر مراقب الأداء أيضًا عبر قائمة المطور. إذا كانت إعادة العرض تستغرق أقل من 1 مللي ثانية — useMemo غير ضروري.

الخلاصة

  • useMemo — خطاف React لتخزين القيم مؤقتًا، يخبئ نتيجة دالة بين عمليات إعادة العرض
  • الخطاف يستقبل دالة حسابية ومصفوفة تبعيات، ويعيد حساب القيمة فقط عند تغيرها
  • في React Native، useMemo مهم بشكل خاص لتحسين القوائم والرسوم المتحركة والحسابات المعقدة
  • السيناريوهات الرئيسية: حسابات مكلفة، props ثابتة لـ React.memo، أنماط ديناميكية
  • useMemo يختلف عن useCallback: الأول يُرجع قيمة، والثاني — دالة
  • الإفراط في useMemo يضعف الأداء بسبب الحمل الإضافي لتخزين التبعيات ومقارنتها
  • قبل إضافة useMemo، قس المشكلة دائمًا باستخدام React DevTools Profiler أو مراقب الأداء في React Native

سنقوم بتطوير تطبيق جوال جاهز

تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.

مناقشة المشروع

اقرأ أيضًا