طَبِّقْهاتعلّم المبدأ، وطبّقه بيدكمقرر 251 · برمجة الأجهزة الذكية
الدرس 24 · الوحدة 11: تحسين أداء التطبيقات وتقليل البطء

تقليل إعادة الرسم: memo والتخزين المؤقت

أدوات تمنع العمل الزائد وتوفّر طاقة المعالج. ⏱ نحو 40 دقيقة.

بعد هذا الدرس ستستطيع أن:
  • تمنع إعادة الرسم غير الضروريّة بـ React.memo.
  • تحفظ الحسابات الثقيلة بـ useMemo.
  • تعرف دور useCallback مع مكوّنات memo.

١إعادة الرسم غير الضروريّة

حين تتغيّر حالة مكوّنٍ أب، يُعاد رسم كل أبنائه — حتى من لم تتغيّر بياناته. على الهاتف هذا يهدر المعالج والبطارية. ثلاث أدواتٍ تعالج ذلك:

الأداةتمنعمتى
React.memoإعادة رسم مكوّنٍ لم تتغيّر خصائصهمكوّن ابنٍ ثقيل
useMemoإعادة حسابٍ ثقيلٍ كل رسمعمليّة مكلفة
useCallbackإنشاء دالّةٍ جديدة كل رسمدالّة تُمرَّر لمكوّنٍ memo

٢React.memo: ابنٌ لا يُعاد رسمه

نغلّف المكوّن بـ React.memo فلا يُعاد رسمه إلّا إذا تغيّرت خصائصه. افتح Console: العدّاد يتغيّر لكن البطاقة الثابتة لا تُطبع ثانيةً:

import { useState, memo } from 'react';import { View, Text, Pressable, StyleSheet } from 'react-native'; const InfoCard = memo(function InfoCard({ title }) {  console.log('رُسِمت البطاقة:', title);     // يُطبع مرّةً فقط  return <Text style={styles.card}>{title}</Text>;}); export default function App() {  const [n, setN] = useState(0);  return (    <View style={styles.box}>      <Text style={styles.n}>العدّاد: {n}</Text>      <InfoCard title="بطاقة ثابتة" />      <Pressable style={styles.btn} onPress={() => setN(n + 1)}>        <Text style={styles.bt}>زيادة</Text>      </Pressable>    </View>  );} const styles = StyleSheet.create({  box: { padding: 24, alignItems: 'center', gap: 10 },  n: { fontSize: 24, fontWeight: 'bold' },  card: { backgroundColor: '#FFF3E0', padding: 12, borderRadius: 10, color: '#E8820C', fontWeight: 'bold' },  btn: { backgroundColor: '#E8820C', padding: 10, borderRadius: 8 },  bt: { color: 'white' },});

اضغط «زيادة» وراقب Console: «رُسِمت البطاقة» تظهر مرّةً فقط رغم تغيّر العدّاد — memo منع إعادة الرسم.

٣useMemo: حسابٌ مكلفٌ مرّةً

useMemo يحفظ نتيجة حسابٍ ثقيل، فلا يُعاد إلّا إذا تغيّرت مدخلاته:

import { useState, useMemo } from 'react';import { View, Text, TextInput, StyleSheet } from 'react-native'; function heavySum(n) {  let s = 0;  for (let i = 0; i < n * 1000; i++) s += i;   // عمليّة مكلفة  return s;} export default function App() {  const [n, setN] = useState('5');  const [other, setOther] = useState('');  const num = Number(n) || 0;  const result = useMemo(() => heavySum(num), [num]);   // يُحسب فقط عند تغيّر num   return (    <View style={{ padding: 20, gap: 10 }}>      <TextInput style={styles.in} keyboardType="numeric" value={n} onChangeText={setN} placeholder="رقم" />      <Text style={styles.r}>نتيجة الحساب الثقيل: {result}</Text>      <TextInput style={styles.in} value={other} onChangeText={setOther} placeholder="اكتب هنا (لا يعيد الحساب)" />    </View>  );} const styles = StyleSheet.create({  in: { borderWidth: 1, borderColor: '#ccc', borderRadius: 8, padding: 8 },  r: { fontWeight: 'bold', color: '#2E7D32' },});

الكتابة في الحقل الثاني لا تعيد الحساب الثقيل، لأن مدخلاته (num) لم تتغيّر. هذا جوهر useMemo.

توقّع وتحقّق

React.memo يمنع:

useMemo يحفظ:

useCallback يثبّت:

متى نحسّن؟

الخلاصة

تحدٍّ برمجي: بطاقة memo

متقدم

امنع إعادة رسم مكوّنٍ ثابت بـ memo.

المطلوب:
  • عرّف مكوّن Label مغلَّفًا بـ memo يستقبل prop اسمه text.
  • يعرض <Text testID="label"> فيه النصّ.
  • استخدمه في App بـ text="ثابت" بجانب عدّادٍ متغيّر.
👀 اعرض الناتج المطلوب (وجرّبه)
✓ التحقق الآلي من حلّك:
  • الكود يعمل بلا أخطاء في Console
  • النصّ «ثابت»
  • استُخدم memo
  • يستقبل prop text

💡 تلميح 1
const Label = memo(function Label({{ text }}) {{ … }}).
💡 تلميح 2
memo يمنع إعادة رسم Label عند تغيّر العدّاد.

حاول بنفسك أولًا؛ للمسألة أكثر من حلّ صحيح.

import { useState, memo } from 'react';import { View, Text, Pressable, StyleSheet } from 'react-native'; const Label = memo(function Label({ text }) {  return <Text testID="label" style={styles.l}>{text}</Text>;}); export default function App() {  const [n, setN] = useState(0);  return (    <View style={{ padding: 20, gap: 10, alignItems: 'center' }}>      <Text style={styles.n}>{n}</Text>      <Label text="ثابت" />      <Pressable style={styles.btn} onPress={() => setN(n + 1)}>        <Text style={styles.bt}>زيادة</Text>      </Pressable>    </View>  );} const styles = StyleSheet.create({  n: { fontSize: 28, fontWeight: 'bold', color: '#E8820C' },  l: { backgroundColor: '#FFF3E0', padding: 8, borderRadius: 8 },  btn: { backgroundColor: '#E8820C', padding: 10, borderRadius: 8 },  bt: { color: 'white' },});
انتقل إلى تقييم الدرساختيار من متعدد، وصح وخطأ، وأكمل الفراغ، وتتبّع الناتج.ابدأ التقييم ←

📝 معلم؟ أرسل ملاحظة للمؤلف عن هذا الدرس