العربية

دليل شامل لتحسين أداء تطبيقات React باستخدام useMemo و useCallback و React.memo. تعلم كيفية منع عمليات إعادة التصيير غير الضرورية وتحسين تجربة المستخدم.

تحسين أداء React: إتقان useMemo و useCallback و React.memo

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

فهم إعادة التصيير في React

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

لماذا تتم إعادة تصيير المكونات؟

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

تقديم useMemo: تخزين نتائج الحسابات المكلفة مؤقتاً

useMemo هو خطاف (hook) في React يقوم بتخزين نتيجة عملية حسابية. التخزين المؤقت (Memoization) هو أسلوب تحسين يقوم بتخزين نتائج استدعاءات الدوال المكلفة وإعادة استخدام تلك النتائج عندما تحدث نفس المدخلات مرة أخرى. هذا يمنع الحاجة إلى إعادة تنفيذ الدالة بشكل غير ضروري.

متى نستخدم useMemo

كيف يعمل useMemo

يأخذ useMemo وسيطتين:

  1. دالة تقوم بإجراء الحساب.
  2. مصفوفة من الاعتماديات.

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

مثال: حساب متتالية فيبوناتشي

متتالية فيبوناتشي هي مثال كلاسيكي لعملية حسابية مكثفة. لنقم بإنشاء مكون يحسب رقم فيبوناتشي n باستخدام useMemo.


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

function Fibonacci({ n }) {
  const fibonacciNumber = useMemo(() => {
    console.log('Calculating Fibonacci...'); // يوضح متى يتم تشغيل الحساب
    function calculateFibonacci(num) {
      if (num <= 1) {
        return num;
      }
      return calculateFibonacci(num - 1) + calculateFibonacci(num - 2);
    }
    return calculateFibonacci(n);
  }, [n]);

  return 

Fibonacci({n}) = {fibonacciNumber}

; } function App() { const [number, setNumber] = useState(5); return (
setNumber(parseInt(e.target.value))} />
); } export default App;

في هذا المثال، يتم تنفيذ الدالة calculateFibonacci فقط عندما تتغير خاصية n. بدون useMemo، سيتم تنفيذ الدالة عند كل إعادة تصيير لمكون Fibonacci، حتى لو بقيت n كما هي. تخيل أن هذا الحساب يحدث على لوحة تحكم مالية عالمية - كل حركة في السوق تسبب إعادة حساب كاملة، مما يؤدي إلى تأخير كبير. يمنع useMemo ذلك.

تقديم useCallback: تخزين الدوال مؤقتاً

useCallback هو خطاف آخر في React يقوم بتخزين الدوال مؤقتًا. يمنع إنشاء نسخة جديدة من الدالة عند كل تصيير، وهو أمر مفيد بشكل خاص عند تمرير دوال الاستدعاء (callbacks) كخصائص إلى المكونات الفرعية.

متى نستخدم useCallback

كيف يعمل useCallback

يأخذ useCallback وسيطتين:

  1. الدالة التي سيتم تخزينها.
  2. مصفوفة من الاعتماديات.

يتم إعادة إنشاء الدالة فقط عندما تتغير إحدى الاعتماديات في المصفوفة. بخلاف ذلك، يُرجع useCallback نفس نسخة الدالة.

مثال: التعامل مع نقرة زر

لنقم بإنشاء مكون به زر يقوم بتشغيل دالة استدعاء. سنستخدم useCallback لتخزين دالة الاستدعاء مؤقتًا.


import React, { useState, useCallback } from 'react';

function Button({ onClick, children }) {
  console.log('Button re-rendered'); // يوضح متى يتم إعادة تصيير الزر
  return ;
}

const MemoizedButton = React.memo(Button);

function App() {
  const [count, setCount] = useState(0);

  const handleClick = useCallback(() => {
    console.log('Button clicked');
    setCount((prevCount) => prevCount + 1);
  }, []); // مصفوفة الاعتماديات الفارغة تعني أن الدالة تُنشأ مرة واحدة فقط

  return (
    

Count: {count}

Increment
); } export default App;

في هذا المثال، يتم إنشاء الدالة handleClick مرة واحدة فقط لأن مصفوفة الاعتماديات فارغة. عندما يتم إعادة تصيير مكون App بسبب تغيير حالة count، تظل الدالة handleClick كما هي. مكون MemoizedButton، المغلف بـ React.memo، سيعيد التصيير فقط إذا تغيرت خصائصه. نظرًا لأن خاصية onClick (handleClick) تظل كما هي، فإن مكون Button لا يعيد التصيير بشكل غير ضروري. تخيل تطبيق خرائط تفاعلي. في كل مرة يتفاعل فيها المستخدم، قد يتأثر العشرات من مكونات الأزرار. بدون useCallback، ستتم إعادة تصيير هذه الأزرار بشكل غير ضروري، مما يخلق تجربة بطيئة. يضمن استخدام useCallback تفاعلًا أكثر سلاسة.

تقديم React.memo: تخزين المكونات مؤقتاً

React.memo هو مكون عالي الرتبة (HOC) يقوم بتخزين مكون وظيفي مؤقتًا. يمنع المكون من إعادة التصيير إذا لم تتغير خصائصه. هذا مشابه لـ PureComponent للمكونات من نوع class.

متى نستخدم React.memo

كيف يعمل React.memo

يقوم React.memo بتغليف مكون وظيفي ويقارن بشكل سطحي بين الخصائص السابقة والتالية. إذا كانت الخصائص متطابقة، فلن يتم إعادة تصيير المكون.

مثال: عرض ملف تعريف المستخدم

لنقم بإنشاء مكون يعرض ملف تعريف المستخدم. سنستخدم React.memo لمنع عمليات إعادة التصيير غير الضرورية إذا لم تتغير بيانات المستخدم.


import React from 'react';

function UserProfile({ user }) {
  console.log('UserProfile re-rendered'); // يوضح متى يتم إعادة تصيير المكون
  return (
    

Name: {user.name}

Email: {user.email}

); } const MemoizedUserProfile = React.memo(UserProfile, (prevProps, nextProps) => { // دالة مقارنة مخصصة (اختياري) return prevProps.user.id === nextProps.user.id; // أعد التصيير فقط إذا تغير معرف المستخدم }); function App() { const [user, setUser] = React.useState({ id: 1, name: 'John Doe', email: 'john.doe@example.com', }); const updateUser = () => { setUser({ ...user, name: 'Jane Doe' }); // تغيير الاسم }; return (
); } export default App;

في هذا المثال، سيعيد مكون MemoizedUserProfile التصيير فقط إذا تغيرت خاصية user.id. حتى لو تغيرت خصائص أخرى في كائن user (مثل الاسم أو البريد الإلكتروني)، فلن يعيد المكون التصيير ما لم يكن المعرف مختلفًا. تتيح دالة المقارنة المخصصة هذه داخل `React.memo` تحكمًا دقيقًا في وقت إعادة تصيير المكون. تخيل منصة وسائط اجتماعية يتم فيها تحديث ملفات تعريف المستخدمين باستمرار. بدون `React.memo`، سيؤدي تغيير حالة المستخدم أو صورة ملفه الشخصي إلى إعادة تصيير كاملة لمكون الملف الشخصي، حتى لو بقيت تفاصيل المستخدم الأساسية كما هي. يسمح `React.memo` بإجراء تحديثات مستهدفة ويحسن الأداء بشكل كبير.

الجمع بين useMemo و useCallback و React.memo

تكون هذه التقنيات الثلاث أكثر فعالية عند استخدامها معًا. يقوم useMemo بتخزين الحسابات المكلفة، و useCallback بتخزين الدوال، و React.memo بتخزين المكونات. من خلال الجمع بين هذه التقنيات، يمكنك تقليل عدد عمليات إعادة التصيير غير الضرورية بشكل كبير في تطبيق React الخاص بك.

مثال: مكون معقد

لنقم بإنشاء مكون أكثر تعقيدًا يوضح كيفية الجمع بين هذه التقنيات.


import React, { useState, useCallback, useMemo } from 'react';

function ListItem({ item, onUpdate, onDelete }) {
  console.log(`ListItem ${item.id} re-rendered`); // يوضح متى يتم إعادة تصيير المكون
  return (
    
  • {item.text}
  • ); } const MemoizedListItem = React.memo(ListItem); function List({ items, onUpdate, onDelete }) { console.log('List re-rendered'); // يوضح متى يتم إعادة تصيير المكون return (
      {items.map((item) => ( ))}
    ); } const MemoizedList = React.memo(List); function App() { const [items, setItems] = useState([ { id: 1, text: 'Item 1' }, { id: 2, text: 'Item 2' }, { id: 3, text: 'Item 3' }, ]); const handleUpdate = useCallback((id) => { setItems((prevItems) => prevItems.map((item) => item.id === id ? { ...item, text: `Updated ${item.text}` } : item ) ); }, []); const handleDelete = useCallback((id) => { setItems((prevItems) => prevItems.filter((item) => item.id !== id)); }, []); const memoizedItems = useMemo(() => items, [items]); return (
    ); } export default App;

    في هذا المثال:

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

    تقنيات تحسين إضافية

    بينما تعد useMemo و useCallback و React.memo أدوات قوية، إلا أنها ليست الخيارات الوحيدة لتحسين أداء React. إليك بعض التقنيات الإضافية التي يجب مراعاتها:

    اعتبارات عالمية للتحسين

    عند تحسين تطبيقات React لجمهور عالمي، من المهم مراعاة عوامل مثل زمن انتقال الشبكة وإمكانيات الجهاز والتوطين. إليك بعض النصائح:

    الخاتمة

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

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