"بطل تكتب useEffect لجلب البيانات. الـ use hook الجديد غيّر كل حاجة. هنا إزاي تجمعها مع Suspense لتطبيقات React أنظف وأسرع."
المشكلة اللي عانت منها React لسنين
لسنين، كنا بنعاني مع البيانات الـ async في React. useEffect hooks، حالات التحميل، معالجة الأخطاء، دوال التنظيف—الفوضى كلها. كانت بتشتغل، بس كنت تحس إنها غلط. عندك مكون محتاج بيانات، فبتكتب useEffect، بتضيف حالة تحميل، بتتعامل مع الأخطاء، وتدعي إنك منسيتش التنظيف.
بعدين جات React 19 مع الـ use hook. وكل حاجة اتغيرت.
الحقيقة: الـ use hook مش بس hook عادي. ده طريقة مختلفة تماماً للتفكير في البيانات الـ async في React. مجمع مع Suspense، بيخلي جلب البيانات يبدو طبيعي—زي ما كان المفروض يكون كدا من الأول.
الطريقة القديمة: جحيم useEffect
فاكر ده؟
// الطريقة القديمة - كابوس useEffect
function UserPosts({ userId }) {
const [posts, setPosts] = useState([])
const [isLoading, setIsLoading] = useState(true)
const [error, setError] = useState(null)
useEffect(() => {
let cancelled = false
async function fetchPosts() {
try {
setIsLoading(true)
const data = await getPosts(userId)
if (!cancelled) {
setPosts(data)
}
} catch (err) {
if (!cancelled) {
setError(err)
}
} finally {
if (!cancelled) {
setIsLoading(false)
}
}
}
fetchPosts()
return () => {
cancelled = true
}
}, [userId])
if (isLoading) return <div>جاري التحميل...</div>
if (error) return <div>خطأ: {error.message}</div>
return (
<ul>
{posts.map(post => <li key={post.id}>{post.title}</li>)}
</ul>
)
}
ده أكتر من 30 سطر من الـ boilerplate. لجلب بيانات واحد. ومغطيناش الـ race conditions بشكل صحيح.
الطريقة الجديدة: use + Suspense
شوف ده:
// الطريقة الجديدة - نظيف وبسيط
import { use, Suspense } from 'react'
function UserPosts({ postsPromise }) {
const posts = use(postsPromise)
return (
<ul>
{posts.map(post => <li key={post.id}>{post.title}</li>)}
</ul>
)
}
function UserPostsPage({ userId }) {
const postsPromise = getPosts(userId)
return (
<Suspense fallback={<div>جاري التحميل...</div>}>
<UserPosts postsPromise={postsPromise} />
</Suspense>
)
}
خلاص كده. مفيش حالة تحميل. مفيش حالة خطأ. مفيش تنظيف. بس بيانات.
إيه هو الـ use Hook؟
الـ use hook هو API جديد في React 19 بياخد قيمة Promise أو Context مباشرة في مرحلة الـ render بتاعة المكون بتاعك. على عكس الـ hooks التقليدية، ممكن تستدعيه جوا conditionals و loops، وبيتكامل بشكل طبيعي مع Suspense وError Boundaries.
import { use } from 'react'
function Message({ messagePromise }) {
const messageContent = use(messagePromise)
return <p>هنا الرسالة: {messageContent}</p>
}
لما use بتتنده مع Promise، المكون بيعمل suspend لحد ما الـ Promise تتحل. غلفه في Suspense boundary، و React بتتعامل مع حالة التحميل تلقائياً.
كسر قواعد الـ Hooks (بطريقة كويسة)
الـ use hook بيكسر "قواعد الـ Hooks" التقليدية في حاجة مهمة—ممكن يستدعى بشكل شرطي:
// ✅ use ممكن تكون شرطية
function Component({ shouldFetch, fetchPromise }) {
if (shouldFetch) {
const data = use(fetchPromise)
return <div>{data}</div>
}
return <div>ما في بيانات محتاجة</div>
}
// ✅ use ممكن تكون في loops
function DataList({ promises }) {
return (
<ul>
{promises.map(promise => {
const data = use(promise)
return <li key={data.id}>{data.name}</li>
})}
</ul>
)
}
المرونة دي بتخلي use مناسبة بشكل فريد لسيناريوهات جلب البيانات الشرطية حيث الـ hooks التقليدية كانت محتاجة حلول معقدة.
معالجة الأخطاء: ليه try/catch ما بتشتغلش
في مشكلة: try/catch ما بتشتغل مع use. ليه؟ لأن use بتعمل suspend، مش بترمي.
// ❌ ده ما بيشتغل - use بتعمل suspend، مش بترمي
function Component({ promise }) {
try {
const data = use(promise)
return <div>{data}</div>
} catch (error) {
return <div>فشل التحميل</div> // مش قابل للوصول!
}
}
بدل كدا، استخدم Error Boundaries مع Suspense:
import { use, Suspense } from "react"
import { ErrorBoundary } from "react-error-boundary"
function Message({ messagePromise }) {
const content = use(messagePromise)
return <p>هنا الرسالة: {content}</p>
}
export function MessageContainer({ messagePromise }) {
return (
<ErrorBoundary fallback={<p> حصل خطأ ما</p>}>
<Suspense fallback={<p> جاري تحميل الرسالة...</p>}>
<Message messagePromise={messagePromise} />
</Suspense>
</ErrorBoundary>
)
}
دلوقتي عندك حالات تحميل وحالات خطأ متعاملة بشكل تصريحي. مفيش كود إلزامي. مفيش إدارة حالة. بس React.
نمط السيرفر/العميل: حيث use بتلمع فعلاً
الـ use hook مثالية لجمع Server و Client Components. اعمل Promises في Server Components ومررها لـ Client Components:
Server Component
// app/page.tsx - Server Component
import db from './database'
async function Page({ id }) {
// انتظر البيانات الحرجة مباشرة
const note = await db.notes.get(id)
// ما تنتظرش - مرر الـ promise للعميل
const commentsPromise = db.comments.get(note.id)
return (
<div>
<h1>{note.title}</h1>
<p>{note.content}</p>
<Suspense fallback={<p>جاري تحميل التعليقات...</p>}>
<Comments commentsPromise={commentsPromise} />
</Suspense>
</div>
)
}
Client Component
// components/Comments.tsx - Client Component
"use client"
import { use } from 'react'
function Comments({ commentsPromise }) {
// بيستكمل الـ promise من السيرفر
// بيعمل suspend لحد ما البيانات تكون متاحة
const comments = use(commentsPromise)
return (
<ul>
{comments.map(comment => (
<li key={comment.id}>{comment.text}</li>
))}
</ul>
)
}
الـ Server Component بتبدأ جلب البيانات. الـ Client Component بتكمله. مفيش طلبات مكررة. مفيش waterfalls. بس تدفق بيانات سلس.
قراءة Context مع use
الـ use hook بتشتغل كمان مع React Context، بتدي syntax أنظف:
import { use } from 'react'
import { ThemeContext } from './ThemeContext'
function ThemedButton() {
const theme = use(ThemeContext)
return <button className={theme}>اضغط هنا</button>
}
وأيوا، تقدر تستخدمها بشكل شرطي مع Context برضو:
function Header({ showTheme }) {
return (
<header>
{showTheme && <ThemeDisplay />}
</header>
)
}
function ThemeDisplay() {
const theme = use(ThemeContext)
return <span>الثيم الحالي: {theme}</span>
}
مثال من الحياة الحقيقية: لوحة تحكم
نبني حاجة حقيقية. لوحة تحكم فيها:
معلومات المستخدم (حرجة، متنتظرة)
إحصائيات (مخزنة مؤقتاً، ممررة كـ promise)
تغذية النشاط (ديناميكية، بتتدفق)
// app/dashboard/page.tsx
import { Suspense } from 'react'
import { db } from '@/lib/db'
import UserInfo from './user-info'
import Stats from './stats'
import ActivityFeed from './activity'
export default async function DashboardPage({ userId }) {
// بيانات حرجة - انتظر مباشرة
const user = await db.users.get(userId)
// بيانات غير حرجة - مرر promises
const statsPromise = db.stats.get(userId)
const activityPromise = db.activity.get(userId)
return (
<div>
<UserInfo user={user} />
<Suspense fallback={<StatsSkeleton />}>
<Stats statsPromise={statsPromise} />
</Suspense>
<Suspense fallback={<ActivitySkeleton />}>
<ActivityFeed activityPromise={activityPromise} />
</Suspense>
</div>
)
}
// app/dashboard/stats.tsx
'use client'
import { use } from 'react'
export default function Stats({ statsPromise }) {
const stats = use(statsPromise)
return (
<div className="grid grid-cols-3 gap-4">
<div>
<h3>إجمالي المشاهدات</h3>
<p>{stats.views}</p>
</div>
<div>
<h3>المتابعون</h3>
<p>{stats.followers}</p>
</div>
<div>
<h3>المنشورات</h3>
<p>{stats.posts}</p>
</div>
</div>
)
}
// app/dashboard/activity.tsx
'use client'
import { use } from 'react'
export default function ActivityFeed({ activityPromise }) {
const activities = use(activityPromise)
return (
<ul>
{activities.map(activity => (
<li key={activity.id}>
<span>{activity.action}</span>
<span>{activity.timestamp}</span>
</li>
))}
</ul>
)
}
الصفحة بتحمل فوراً مع معلومات المستخدم. تتدفق الإحصائيات والنشاطات بمجرد وصولها. مفيش spinners تحميل بتحجب الصفحة كلها. بس تحسين تدريجي.
مقارنة: use مقابل الأنماط التقليدية
النمط | useEffect + useState | use + Suspense |
|---|---|---|
سطور الكود | 30+ | 5-10 |
حالة التحميل | يدوية | تلقائية |
معالجة الأخطاء | try/catch يدوي | Error Boundary |
أفضل الممارسات
اعمل Promises في Server Components: يفضل تعمل Promises على السيرفر وتمررها لـ Client Components.
استخدم async/await في Server Components: لما بتجيب بيانات في Server Component، استخدم
async/awaitمباشرة:typescriptexample.typescript// Server Component - استخدم async/await async function Note({ id }) { const note = await db.notes.get(id) return <div>{note.content}</div> }قم دائماً بالتغليف بـ Suspense و Error Boundaries: وفر بدائل (fallbacks) ذات معنى
typescriptexample.typescript<ErrorBoundary fallback={<ErrorFallback />}> <Suspense fallback={<LoadingSpinner />}> <DataComponent /> </Suspense> </ErrorBoundary>ما تبالغش: استخدم
useللبيانات اللي بتستفيد من تكامل Suspense. للحالة البسيطة من جهة Client،useStateلسه الاختيار الصح.مرر promises، مش دوال جلب: اعمل الـ promise بره ومرره:
typescriptexample.typescript// ✅ صح const promise = fetchData() <Component dataPromise={promise} /> // ❌ تجنب <Component fetchFn={fetchData} />
متى تستخدم use
استخدم الـ use hook لما:
بتشتغل مع Server Components وعايز تمرر promises لـ Client Components
محتاج جلب بيانات شرطي أو قائم على الـ loop
عايز تكامل تلقائي مع Suspense
بتقرأ Context بطريقة أنظف
لا تستخدمه عندما:
محتاج state من جهة client ما فيهاش بيانات async
ما بتستخدمش React 19+
محتاج تحكم دقيق في دورة حياة جلب البيانات
الخلاصة
يمثل use hook تحولاً جذريًا في كيفية تعاملنا مع البيانات غير المتزامنة في React. لما بندمجه مع Suspense، فبيقضي على التعليمات البرمجية الروتينية التي أرهقت تطوير تطبيقات React لسنوات.
مفيش مزيد من تنظيف useEffect. مفيش مزيد من إدارة حالات التحميل. مفيش مزيد من حالات السباق. مجرد جلب بيانات تعريفي يبدو طبيعياً.
React 19 ما أضافتش بس hook جديد. غيرت اللعبة.
المصادر:
ترميز سعيد! — Ahmed Fahmy