React: تحسين الأداء: useMemo و useCallback

آخر تحديث: 2026-08-26

1. ما ستتعلمه



2. قصة صفحة تصفية البحث

العلاقة بين المكونات الثلاثة لتحسين الأداء

100%
flowchart TD
    A[Parent Component Rendering] --> B{Props It's changed?}
    B -->|Yes| C[React.memo Intercept?]
    B -->|No| D[Skip subcomponents ✅]
    C -->|A rough comparison is roughly equal| D
    C -->|Shallow Comparison Inequality| E{Function/The object is new.?}
    E -->|Yes| F[useCallback/useMemo]
    F --> G[Stable Citation → memo Effective ✅]
    E -->|No| D
    
    style F fill:#e8f5e9,stroke:#2e7d32
    style D fill:#c8e6c9,stroke:#2e7d32
    style G fill:#c8e6c9,stroke:#2e7d32

(1) المشكلة: إعادة الحساب مع كل إدخال

أنشأت أليس صفحة بحث عن المنتجات تضم 10,000 منتج يمكن تصفية النتائج منها:

JSX
function ShopPage() {
  const [products] = useState(generate10000Products())
  const [search, setSearch] = useState('')
  const [sortBy, setSortBy] = useState('name')

  // ❌ Question:Re-filter after each input 10,000 Items
  const filtered = products
    .filter(p => p.name.includes(search))
    .sort((a, b) => a[sortBy] > b[sortBy] ? 1 : -1)

  return (
    <div>
      <input value={search} onChange={e => setSearch(e.target.value)} />
      <select value={sortBy} onChange={e => setSortBy(e.target.value)}>
        <option value="name">By Name</option>
        <option value="price">By Price</option>
      </select>
      <ProductList products={filtered} />
    </div>
  )
}
▶ جرّب الكود

المشكلة: في كل مرة يتم فيها إدخال قيمة من لوحة المفاتيح (setSearch)، يتسبب ذلك في تشغيل filter وsort مرة أخرى — ويتعين تصفية 10,000 سجل في كل مرة، مما يؤدي إلى بطء أداء الواجهة.

(2) حل useMemo

JSX
function ShopPage() {
  const [products] = useState(generate10000Products())
  const [search, setSearch] = useState('')
  const [sortBy, setSortBy] = useState('name')

  // ✅ useMemo:Only at search or  sortBy Recalculate only when there is a change
  const filtered = useMemo(() => {
    console.log('Refilter...(Only when the search terms or sorting criteria change)')
    return products
      .filter(p => p.name.includes(search))
      .sort((a, b) => a[sortBy] > b[sortBy] ? 1 : -1)
  }, [search, sortBy, products])

  return (
    <div>
      <input value={search} onChange={e => setSearch(e.target.value)} />
      <select value={sortBy} onChange={e => setSortBy(e.target.value)}>
        <option value="name">By Name</option>
        <option value="price">By Price</option>
      </select>
      <ProductList products={filtered} />
    </div>
  )
}
▶ جرّب الكود

المزايا: لا يتم إعادة تشغيل عملية التصفية إلا عندما يتغير المدخل إلى search أو sortBy؛ أما التغييرات الأخرى في الحالة (مثل إعادة عرض المكون الأصلي) فلا تؤدي إلى تشغيل عملية التصفية، مما يؤدي إلى تحسن في الأداء بمقدار 10 إلى 100 ضعف.



3. useMemo: تخزين نتائج الحسابات مؤقتًا

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

(1) قواعد النحو الأساسية

JSX
const memoizedValue = useMemo(() => {
  return Costly Computations()
}, [Dependency1, Dependency2])
▶ جرّب الكود
القسم الوصف
() => Computation دالة يجب تخزينها مؤقتًا وتُرجع نتيجة الحساب
[Dependencies] إعادة الحساب عند تغير التبعيات
memoizedValue النتائج المخزنة مؤقتًا (تُرجع النتيجة السابقة مباشرةً إذا لم تتغير التبعيات)

(2) السيناريوهات التي ينطبق عليها ذلك

JSX
// Scene 1:Data Conversion(Filter、Sort、Grouping)
const activeUsers = useMemo(
  () => users.filter(u => u.isActive).sort((a, b) => a.name.localeCompare(b.name)),
  [users]
)

// Scene 2:Format(Numbers、Date、String Concatenation)
const formattedPrice = useMemo(
  () => new Intl.NumberFormat('zh-CN', { style: 'currency', currency: 'CNY' }).format(price),
  [price]
)

// Scene 3:Derived State(Calculating New Data from Existing Data)
const statistics = useMemo(() => ({
  total: orders.length,
  paid: orders.filter(o => o.status === 'paid').length,
  amount: orders.reduce((sum, o) => sum + o.amount, 0),
  avgAmount: orders.length ? orders.reduce((sum, o) => sum + o.amount, 0) / orders.length : 0
}), [orders])
▶ جرّب الكود

▶ مثال: مقارنة الأداء

JSX
// ============================================
// Example:useMemo vs no useMemo Performance Comparison
// ============================================

function ExpensiveCalc({ num, unrelated }) {
  // ❌ None useMemo:Executed on every render
  const slowResult1 = slowFibonacci(num)

  // ✅ With useMemo: num If it hasn't changed, don't recalculate it.
  const slowResult2 = useMemo(() => slowFibonacci(num), [num])

  return (
    <div>
      <p>num={num} The Fibonacci sequence</p>
      <p>Without useMemo: {slowResult1}(Rendering calculations each time)</p>
      <p>With useMemo: {slowResult2}(Only at num Calculate when changes occur)</p>
      <p>Unrelated Status:{unrelated}(Click the button to change this value)</p>
    </div>
  )
}

function slowFibonacci(n) {
  if (n <= 1) return n
  let a = 0, b = 1, c
  for (let i = 2; i <= n; i++) { c = a + b; a = b; b = c }
  console.log('fibonacci Calculation!n=' + n)
  return b
}
// Click to Make a Difference unrelated → without useMemo recalculates,with useMemo skips
▶ جرّب الكود

4. useCallback: حفظ مراجع الدوال

(1) قواعد النحو الأساسية

JSX
const memoizedFn = useCallback(() => {
  doSomething(a, b)
}, [a, b])
▶ جرّب الكود

useCallback(fn, deps) تعادل useMemo(() => fn, deps) — فما يتم تخزينه هو الدالة نفسها، وليس نتيجة الدالة.

(2) لماذا يُعد useCallback ضروريًا؟

JSX
function Parent() {
  const [count, setCount] = useState(0)

  // ❌ Every time Parent Rendering,handleClick These are all new functions
  // resulting in Child 's  React.memo Failure
  const handleClick = () => {
    console.log('Clicked')
  }

  // ✅ useCallback:handleClick Citation unchanged
  // Child 's  React.memo Works properly(Skip Re-rendering)
  const handleClick = useCallback(() => {
    console.log('Clicked')
  }, [])  // Empty Dependency:This function will never change.
}
▶ جرّب الكود

(3) React.memo: منع إعادة عرض المكونات الفرعية

JSX
// ============================================
// Example:useCallback + React.memo For use in combination with
// ============================================

// ---- Child component:use  React.memo Package ----
const ExpensiveList = React.memo(function ExpensiveList({ items, onItemClick }) {
  console.log('ExpensiveList Render Again!')
  return (
    <ul>
      {items.map(item => (
        <li key={item.id}>
          {item.name}
          <button onClick={() => onItemClick(item.id)}>Select</button>
        </li>
      ))}
    </ul>
  )
})

// ---- Parent Component ----
function App() {
  const [count, setCount] = useState(0)
  const [items] = useState([
    { id: 1, name: 'Project A' },
    { id: 2, name: 'Project B' },
    { id: 3, name: 'Project C' }
  ])

  // ❌ Every time App Rendering,All function references have changed → React.memo Failure
  const handleClickBad = (id) => console.log('Select:', id)

  // ✅ Function references are immutable → React.memo Effective
  const handleClick = useCallback((id) => {
    console.log('Select:', id)
  }, [])

  return (
    <div>
      <p>Counter:{count}</p>
      <button onClick={() => setCount(c => c + 1)}>Change Count</button>
      {/* Tap the button to change the count → No impact ExpensiveList Rendering of */}
      <ExpensiveList items={items} onItemClick={handleClick} />
    </div>
  )
}
// When clicking "Change Count" :
// ❌ Without useCallback → ExpensiveList Render again
// ✅ has  useCallback → ExpensiveList Skip rendering(Performance Improvements)
▶ جرّب الكود

5. متى يجب استخدامه، ومتى لا يجب استخدامه

(1) متى يتم استخدامه

السيناريو العنصر الجاذب السبب
تصفية/فرز البيانات الضخمة useMemo تجنب البحث في كميات كبيرة من البيانات في كل عملية عرض
الحسابات المعقدة (الرياضيات/التنسيق) useMemo لا داعي لإعادة الحساب إذا بقيت النتيجة كما هي
تمرير المعلمات إلى مكون فرعي يستخدم React.memo useCallback الحفاظ على استقرار مراجع الدوال لضمان عمل React.memo
التبعيات في دالة useEffect useCallback منع تنفيذ دالة useEffect عدة مرات بسبب التغييرات في مراجع الدالة

(2) الحالات التي لا تكون فيها هناك حاجة إليها

JSX
// ❌ Not necessary:Simple Calculations
const doubled = useMemo(() => count * 2, [count])
// ✅ Write it directly:count * 2 It's already pretty fast as it is
const doubled = count * 2

// ❌ Not necessary:Handle events without passing them to child components
const handleClick = useCallback(() => {
  console.log('Click')
}, [])
// ✅ Just write it down.
const handleClick = () => console.log('Click')

// ❌ Not necessary:The child component isn't being used. React.memo
// useCallback It's been packaged, but no one is buying it."Stable Citation"
▶ جرّب الكود

(3) تكلفة الإساءة

JSX
// useMemo and  useCallback It involves expenses(Relies heavily on arrays)
// Abuse = Introducing Additional Comparison Logic,Slower than if it weren't optimized

// Rule of thumb:
// 1. First, let's write the version without optimization.
// 2. Feeling some lag(fps < 60)Use again
// 3. use  React DevTools Profiler Measurement
▶ جرّب الكود

6. مثال كامل: قائمة الرسائل في تطبيق الدردشة

JSX
// ============================================
// Complete Example:Chat Message List(Comprehensive Performance Optimization)
// ============================================

import { useState, useMemo, useCallback, memo } from 'react'

// ---- News Item(React.memo + useCallback) ----
const MessageItem = memo(function MessageItem({ message, onReact, onReply }) {
  console.log(`Rendering Message:${message.id}`)
  
  return (
    <div style={{
      padding: '12px',
      margin: '8px 0',
      backgroundColor: message.isMine ? '#e6f7ff' : '#f5f5f5',
      borderRadius: '8px'
    }}>
      <div style={{ display: 'flex', justifyContent: 'space-between' }}>
        <strong>{message.author}</strong>
        <span style={{ color: '#999', fontSize: '12px' }}>
          {message.time}
        </span>
      </div>
      <p style={{ margin: '8px 0' }}>{message.text}</p>
      <div style={{ display: 'flex', gap: '8px' }}>
        <button onClick={() => onReact(message.id)} style={smallBtn}>
          👍 {message.reactions}
        </button>
        <button onClick={() => onReply(message.id)} style={smallBtn}>
          💬 Reply
        </button>
      </div>
    </div>
  )
})

// ---- Main Component ----
function ChatApp() {
  const [messages, setMessages] = useState(generateMessages(100))
  const [filter, setFilter] = useState('All')
  const [unreadCount, setUnreadCount] = useState(5)

  // useMemo:Filter Messages(100 Filter by row)
  const filteredMessages = useMemo(
    () => filter === 'All'
      ? messages
      : messages.filter(m => filter === 'My' ? m.isMine : !m.isMine),
    [messages, filter]
  )

  // useCallback:Keep References Consistent,let  MessageItem 's  memo Effective
  const handleReact = useCallback((id) => {
    setMessages(prev => prev.map(m =>
      m.id === id ? { ...m, reactions: m.reactions + 1 } : m
    ))
  }, [])

  const handleReply = useCallback((id) => {
    console.log('Reply to a message:', id)
  }, [])

  // Calculate Statistical Information
  const stats = useMemo(() => ({
    total: messages.length,
    mine: messages.filter(m => m.isMine).length,
    totalReactions: messages.reduce((s, m) => s + m.reactions, 0)
  }), [messages])

  return (
    <div style={{ maxWidth: '600px', margin: '0 auto' }}>
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
        <h2>💬 Chat({unreadCount} Unread posts)</h2>
        <button onClick={() => setUnreadCount(c => c + 1)}>
          Increase the unread count(Test Performance)
        </button>
      </div>

      {/* Statistics(useMemo) */}
      <div style={{ display: 'flex', gap: '16px', marginBottom: '12px', fontSize: '13px', color: '#666' }}>
        <span>Total {stats.total} items</span>
        <span>My {stats.mine} items</span>
        <span>👍 {stats.totalReactions}</span>
      </div>

      {/* Filter Button */}
      <div style={{ marginBottom: '12px' }}>
        {['All', 'My', 'Other people's'].map(f => (
          <button
            key={f}
            onClick={() => setFilter(f)}
            style={{
              padding: '4px 12px', margin: '0 4px',
              backgroundColor: filter === f ? '#1890ff' : '#f0f0f0',
              color: filter === f ? 'white' : '#333',
              border: 'none', borderRadius: '4px', cursor: 'pointer'
            }}
          >
            {f}
          </button>
        ))}
      </div>

      {/* News List */}
      <div style={{ maxHeight: '500px', overflow: 'auto' }}>
        {filteredMessages.map(msg => (
          <MessageItem
            key={msg.id}
            message={msg}
            onReact={handleReact}   // ✅ Stable citation
            onReply={handleReply}   // ✅ Stable citation
          />
        ))}
      </div>
    </div>
  )
}

function generateMessages(count) {
  const names = ['Alice', 'Bob', 'Charlie', 'Diana']
  return Array.from({ length: count }, (_, i) => ({
    id: i + 1,
    author: names[i % 4],
    text: `This is the ${i + 1} The content of the message`,
    time: `${String(10 + i % 8).padStart(2, '0')}:${String(i % 60).padStart(2, '0')}`,
    isMine: i % 3 === 0,
    reactions: Math.floor(Math.random() * 5)
  }))
}

const smallBtn = {
  padding: '2px 8px', fontSize: '12px',
  border: '1px solid #d9d9d9', borderRadius: '4px',
  backgroundColor: 'white', cursor: 'pointer'
}

الناتج المتوقع: قائمة محادثة تحتوي على 100 رسالة. عند النقر على «زيادة عدد الرسائل غير المقروءة»، لن يتم إعادة عرض قائمة الرسائل (لن يظهر أي ناتج في وحدة التحكم) بسبب useCallback وReact.memo؛ بل سيتغير العدد فقط.


▶ المثال 2: تمنع دالة useCallback دالة useEffect من التشغيل عدة مرات

JSX
// ============================================
// Example:useCallback In conjunction with useEffect Avoiding Infinite Loops
// Features:Display useCallback in  useEffect The Key Role of Dependencies
// ============================================

import { useState, useEffect, useCallback } from 'react'

function SearchComponent() {
  const [query, setQuery] = useState('')
  const [results, setResults] = useState([])
  const [page, setPage] = useState(1)

  // ❌ Question:Each render is a new function,As useEffect Dependencies can cause an infinite loop
  // const fetchResults = async () => {
  //   const res = await fetch(`/api/search?q=${query}&page=${page}`)
  //   const data = await res.json()
  //   setResults(data)
  // }

  // ✅ Resolve:useCallback Stable Function References
  const fetchResults = useCallback(async () => {
    // Simulation API Request
    const mockData = [
      { id: 1, title: `Results ${query} - Page ${page}` },
      { id: 2, title: `Related Articles About ${query}` },
      { id: 3, title: `${query} Best Practices` }
    ]
    setResults(mockData)
  }, [query, page])  // query or  page Create a new function only when there is a change

  // Dependency fetchResults(Stable Citation,It won't go into an infinite loop)
  useEffect(() => {
    if (query.trim()) {
      fetchResults()
    }
  }, [fetchResults])  // fetchResults Citation unchanged → useEffect Will not be executed again

  return (
    <div style={{ maxWidth: '500px', margin: '0 auto' }}>
      <h2>🔍 Search(useCallback Demo)</h2>
      <input
        value={query}
        onChange={e => setQuery(e.target.value)}
        placeholder="Enter a search term..."
        style={{ width: '100%', padding: '8px', border: '1px solid #d9d9d9', borderRadius: '4px' }}
      />
      <div style={{ marginTop: '8px', display: 'flex', gap: '8px', alignItems: 'center' }}>
        <button onClick={() => setPage(p => Math.max(1, p - 1))} disabled={page <= 1}>Previous Page</button>
        <span>Page  {page} </span>
        <button onClick={() => setPage(p => p + 1)}>Next Page</button>
      </div>
      <ul style={{ marginTop: '12px', listStyle: 'none', padding: 0 }}>
        {results.map(r => (
          <li key={r.id} style={{ padding: '8px', borderBottom: '1px solid #f0f0f0' }}>
            {r.title}
          </li>
        ))}
      </ul>
      {!query && <p style={{ color: '#999', textAlign: 'center' }}>Enter a keyword to start your search</p>}
    </div>
  )
}

▶ المثال 3: استخدام useMemo لتنفيذ حسابات القوائم الافتراضية

JSX
function VirtualList({ items, itemHeight = 40, visibleHeight = 400 }) {
  const [scrollTop, setScrollTop] = useState(0)

  const visibleCount = Math.ceil(visibleHeight / itemHeight)
  const totalHeight = useMemo(() => items.length * itemHeight, [items.length, itemHeight])

  const { startIndex, endIndex, offsetY } = useMemo(() => {
    const start = Math.floor(scrollTop / itemHeight)
    const end = Math.min(start + visibleCount + 1, items.length)
    return { startIndex: start, endIndex: end, offsetY: start * itemHeight }
  }, [scrollTop, itemHeight, visibleCount, items.length])

  const visibleItems = useMemo(() => items.slice(startIndex, endIndex), [items, startIndex, endIndex])

  return (
    <div onScroll={e => setScrollTop(e.currentTarget.scrollTop)}
      style={{ height: visibleHeight, overflowY: 'auto', border: '1px solid #ddd', borderRadius: 4 }}>
      <div style={{ height: totalHeight, position: 'relative' }}>
        <div style={{ position: 'absolute', top: offsetY, width: '100%' }}>
          {visibleItems.map((item, i) => (
            <div key={startIndex + i} style={{ height: itemHeight, padding: '0 12px', display: 'flex', alignItems: 'center', borderBottom: '1px solid #f0f0f0' }}>
              Row {startIndex + i}: {item}
            </div>
          ))}
        </div>
      </div>
    </div>
  )
}
▶ جرّب الكود

▶ المثال 4: تحسين أداء القوائم باستخدام React.memo + useCallback

JSX
const ListItem = React.memo(function ListItem({ name, price, onRemove }) {
  console.log(`Rendering: ${name}`)
  return (
    <div style={{ display: 'flex', justifyContent: 'space-between', padding: 8, borderBottom: '1px solid #f0f0f0' }}>
      <span>{name} - ${price}</span>
      <button onClick={() => onRemove(name)} style={{ color: '#ff4d4f', border: 'none', cursor: 'pointer' }}>Remove</button>
    </div>
  )
})

function ProductList() {
  const [products, setProducts] = useState([
    { name: 'Keyboard', price: 79 },
    { name: 'Mouse', price: 49 },
    { name: 'Monitor', price: 399 },
    { name: 'Headphones', price: 129 },
  ])
  const [filter, setFilter] = useState('')

  const handleRemove = useCallback((name) => {
    setProducts(prev => prev.filter(p => p.name !== name))
  }, [])

  const filtered = useMemo(() => products.filter(p => p.name.toLowerCase().includes(filter.toLowerCase())), [products, filter])

  return (
    <div style={{ maxWidth: 400, margin: '0 auto' }}>
      <input value={filter} onChange={e => setFilter(e.target.value)} placeholder="Filter..." style={{ width: '100%', padding: 8, marginBottom: 8, borderRadius: 4 }} />
      {filtered.map(p => <ListItem key={p.name} name={p.name} price={p.price} onRemove={handleRemove} />)}
    </div>
  )
}
▶ جرّب الكود

▶ المثال 5: متى لا ينبغي استخدام useMemo — مقارنة مع الأنماط السيئة

JSX
function BadExample({ count }) {
  // ❌ Simple calculations are not required useMemo——Create memo Expenses > The Calculation Itself
  const doubled = useMemo(() => count * 2, [count])
  // ✅ Direct Calculation
  const doubledOk = count * 2

  // ❌ Each render creates a new object as useMemo Dependency
  const config = useMemo(() => ({ count }), [count])
  const badResult = useMemo(() => config.count * 3, [config]) // config A new reference each time!
  // ✅ Using Primitive Types Directly as Dependencies
  const goodResult = useMemo(() => count * 3, [count])

  return <p>Doubled: {doubledOk}, Tripled: {goodResult}</p>
}

function GoodExample({ items, query }) {
  // ✅ Filter/Sorting large amounts of data useMemo
  const filtered = useMemo(() => {
    return items.filter(item =>
      item.name.toLowerCase().includes(query.toLowerCase())
    ).sort((a, b) => a.name.localeCompare(b.name))
  }, [items, query])

  // ✅ Complex objects passed to child components use useMemo
  const chartData = useMemo(() => ({
    labels: filtered.map(i => i.name),
    values: filtered.map(i => i.price),
  }), [filtered])

  return (
    <div>
      <p>Found: {filtered.length} items</p>
      <Chart data={chartData} />
    </div>
  )
}
▶ جرّب الكود

❓ أسئلة شائعة

س هل تعمل useMemo وuseCallback دائمًا على تحسين الأداء؟
ج ليس بالضرورة. فهما تنطويان على عبء إضافي خاص بهما — حيث تقومان بمقارنة المصفوفة dependencies في كل عملية عرض. وقد يؤدي الإفراط في استخدامهما إلى إبطاء الأداء فعليًّا. التوصية الرسمية لـ React: ابدأ بكتابة النسخة غير المُحسَّنة، ولا تقم بالتحسين إلا عندما تلاحظ حدوث تأخير. القاعدة العامة: استخدمهما فقط للحسابات التي يتم تمريرها إلى مكون فرعي من نوع React.memo أو تلك التي تتطلب وقتًا إضافيًا يزيد عن 1 مللي ثانية.
س ما الفرق بين useMemo والمتغير العادي؟
ج يمتلك useMemo «ذاكرة» — فإذا لم تتغير التبعيات، فإنه يستخدم القيمة المخزنة في ذاكرة التخزين المؤقت مباشرةً دون إعادة تقييم التعبير. أما المتغير العادي فيُعاد تقييمه عند كل عملية عرض. ومع ذلك، يحتاج useMemo إلى مقارنة مصفوفة التبعيات أثناء العرض، لذا فإن «الحسابات البسيطة تكون في الواقع أبطأ مع useMemo
س متى يجب استخدام useCallback للحفاظ على مرجع الدالة؟
ج لا تحتاج إلى استخدامه إلا عند استيفاء الشرطين التاليين معًا: ① أن يتم تمرير الدالة كخاصية (prop) إلى مكون فرعي يستخدم React.memo؛ ② أن تكون الدالة موجودة في صفيف التبعيات (dependencies) لـ hook useEffect. إذا لم ينطبق أي من هذين الشرطين، فيمكنك ببساطة كتابة الدالة مباشرةً؛ ولا داعي لاستخدام useCallback.
س ما الفرق بين useMemo وuseCallback؟
ج يقوم useMemo(() => value, deps) بتخزين نتيجة (قيمة) عملية حسابية في ذاكرة التخزين المؤقت، بينما يقوم useCallback(() => fn, deps) بتخزين مرجع الدالة في ذاكرة التخزين المؤقت. وفي الواقع، فإن useCallback(fn, deps) يعادل useMemo(() => fn, deps). استخدم useCallback عندما تحتاج إلى تخزين دالة تم تمريرها إلى مكون فرعي؛ واستخدم useMemo عندما تحتاج إلى تخزين نتيجة عملية حسابية.

📖 ملخص


📝 تمارين

  1. المشكلة الأساسية (الصعوبة ⭐): أنشئ مكونًا PrimeCalculator يتلقى رقمًا n كمدخل ويحسب العدد الأولي رقم n. استخدم useMemo لتخزين النتيجة مؤقتًا حتى لا يتم إعادة حسابها في كل مرة يتم فيها إدخال قيمة جديدة.
  2. تمرين متقدم (مستوى الصعوبة ⭐⭐): أنشئ مكونًا ProductTable يحتوي على 500 سجل منتج ويدعم التصفية حسب الفئة والفرز حسب السعر. استخدم useMemo لتحسين منطق التصفية والفرز، واستخدم useCallback مع React.memo لتحسين عرض القائمة.
  3. التحدي (الصعوبة: ⭐⭐⭐): قم بإنشاء مكون DataDashboard يقوم بتجميع 10,000 سجل طلب عبر ثلاثة أبعاد مختلفة (الوقت، والمنطقة، والفئة)، واستخدم useMemo لتنفيذ التخزين المؤقت الهرمي (بحيث يتم إعادة حساب الطبقات ذات الصلة فقط عند تغير الأبعاد).
Web-Tutorial.com

فريق Web-Tutorial التقني

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

100%