الدرس 1 من 13 · أساسيات React
لماذا React: المكوّنات، والعرض التصريحي، وأول مشروع باستعمال Vite
الهدف
أن تفهم المشكلة التي يحلّها React، وكيف يحدّث الشاشة، وأين يقع ضمن منظومته (Vite وNext.js وReact Router)، ثم أن تبني يدويًا بنية مشروع Vite لتعرف ما يحتويه كل ملف.
أهم النقاط
- React مكتبة JavaScript لبناء الواجهات انطلاقًا من مكوّنات (components): دوالّ تُرجع ترميزًا (markup).
- العرض تصريحي (declarative): تصف الشاشة لحالة معيّنة، ويتكفّل React بتعديل الـ DOM عندما تتغيّر الحالة.
- يعمل React على مرحلتين: العرض (render)، حيث يستدعي مكوّناتك لحساب النتيجة، ثم التثبيت (commit)، حيث لا يطبّق على الـ DOM إلا الفروق.
- لا يتكفّل React «إلا» بالواجهة. أما الأدوات، فنستعمل Vite (خادم التطوير والبناء) أو إطار عمل (framework) مثل Next.js أو React Router.
- يتكوّن مشروع Vite من أربع قطع:
package.jsonوindex.htmlوsrc/main.jsx(نقطة الدخول) وsrc/App.jsx(أول مكوّن).
المشكلة: تحديث الشاشة يدويًا
لنأخذ تعاونية الزرابي Tifawin بأزيلال، التي تبيع عبر الإنترنت. في دورة «Web»، كتبت هذا النوع من الشيفرة لعرض عدد السلع في السلة:
// JavaScript «أمرية» (impérative): نخبر المتصفح بما يجب تغييره، خطوةً بخطوة
let nombre = 0;
const badge = document.querySelector('#badge-panier');
const bouton = document.querySelector('#ajouter');
bouton.addEventListener('click', () => {
nombre = nombre + 1;
badge.textContent = nombre; // لا تنسَ تحديث الشارة…
if (nombre > 0) badge.classList.remove('cache'); // …وظهورها…
// …والمجموع، وزر «Commander»، والملخّص أسفل الصفحة…
});
ما دامت الشاشة تضم ثلاثة عناصر، فكل شيء على ما يرام. لكن مع كتالوج ومرشّحات وسلة واستمارة طلب، يجب على كل حدث أن يتذكّر جميع المناطق التي ينبغي تحديثها. والسهو يتحوّل إلى أخطاء (bugs): الشارة تعرض 3، والملخّص يعرض 2.
جواب React: أن تصف، لا أن تعدّل
مع React، تكتب ما يجب أن تكون عليه الشاشة لقيمة معيّنة. وعندما تتغيّر القيمة، يعيد React الحساب ويحدّث الـ DOM نيابةً عنك:
import { useState } from 'react';
export default function BoutonPanier() {
const [nombre, setNombre] = useState(0); // الحالة: «ذاكرة» المكوّن
return (
<div>
<button onClick={() => setNombre(nombre + 1)}>Ajouter au panier</button>
{nombre > 0 && <span className="badge">{nombre}</span>}
</div>
);
}
لم يعد هناك querySelector ولا textContent: الواجهة أصبحت دالةً للحالة. ويمكن تلخيص ذلك في صيغة: الواجهة = f(الحالة).
المكوّنات: لبنات قابلة لإعادة الاستعمال
المكوّن دالة JavaScript يبدأ اسمها بحرف كبير وتُرجع ترميزًا (من نوع JSX، الذي سنراه في الدرس الموالي). نجمّعها كما نجمّع اللبنات:
function CarteProduit({ nom, prix }) {
return (
<article>
<h2>{nom}</h2>
<p>{prix} DH</p>
</article>
);
}
export default function Catalogue() {
return (
<main>
<CarteProduit nom="Tapis Azilal 2 x 3 m" prix={4200} />
<CarteProduit nom="Coussin en cuir de Fès" prix={350} />
</main>
);
}
يصبح التطبيق بأكمله شجرةً من المكوّنات: App يحتوي على Entete وCatalogue وPanier؛ وCatalogue يحتوي على عدة CarteProduit، وهكذا.
العرض والتثبيت: ما يُسمّى «DOM الافتراضي»
كثيرًا ما نسمع أن React يستعمل «DOM افتراضيًا» (virtual DOM). عمليًا، يجري كل تحديث على ثلاث مراحل:
- الإطلاق (trigger): عند بدء التشغيل (العرض الأولي)، أو عندما تتغيّر حالة ما (
setNombre(...)). - العرض (render): يستدعي React مكوّناتك. فتُرجع وصفًا خفيفًا للواجهة (كائنات JavaScript، وليست عُقد DOM حقيقية). ثم يقارن React هذا الوصف بوصف العرض السابق: هذه هي المطابقة (reconciliation).
- التثبيت (commit): يطبّق React على الـ DOM الحقيقي الفروق فقط. فإذا تغيّرت الشارة وحدها، فلن يُعدَّل إلا نصّ الشارة.
النتيجة العملية: حقل الإدخال الذي يكتب فيه المستخدم نصًا لا «يُمسَح» بسبب عرض جديد، لأن React لا يلمس ما لم يتغيّر.
setNombre(3)
│
▼
Rendu : BoutonPanier() est rappelé → <span>3</span> au lieu de <span>2</span>
│
▼
Commit : un seul changement dans le DOM, le texte du <span>
│
▼
Le navigateur repeint l'écran
(Rendu = العرض: يُستدعى BoutonPanier() من جديد فيُرجع <span>3</span> بدلًا من <span>2</span>؛ Commit = التثبيت: تغيير واحد فقط في الـ DOM هو نصّ الـ <span>؛ ثم يعيد المتصفح رسم الشاشة.)
تذكّر: «العرض» (render) يعني استدعاء مكوّناتك، وليس بالضرورة لمس الـ DOM.
المنظومة: React لا يأتي وحده
يتكفّل React بالواجهة. لكن التطبيق الحقيقي يحتاج أيضًا إلى تحويل JSX، وتجميع الملفات، وتدبير الصفحات، وأحيانًا إلى العرض من جهة الخادم. لذلك نختار أداةً تحيط به:
| الأداة | الدور | متى نختارها |
|---|---|---|
| Vite | خادم تطوير سريع جدًا، وبناء نسخة الإنتاج | تطبيق «من جهة العميل» (SPA): لوحة قيادة، واجهة إدارة (back-office)، متجر يُقدَّم على شكل ملفات ثابتة |
| Next.js | إطار عمل متكامل: صفحات، عرض من جهة الخادم، مسارات API | موقع عمومي يجب أن يكون جيّد الظهور في محركات البحث، عرض من جهة الخادم، مشروع كبير |
| React Router | التنقّل بين الصفحات؛ ويمكن أن يُستعمل أيضًا إطارَ عمل متكاملًا | SPA متعدد الصفحات (الدرس 2.5) |
في هذا التكوين، نستعمل Vite: فهو الأبسط للتعلّم، والنتيجة (ملفات HTML وCSS وJS) يمكن نشرها على أي خادم nginx.
يمكن أيضًا إضافة React إلى جزء من موقع موجود (أداة حجز (widget) داخل صفحة PHP مثلًا): وفي هذه الحالة نركّب React في عنصر واحد فقط من الصفحة.
إنشاء مشروع Vite (مع اتصال بالشبكة)
على جهازك، مع Node.js 20 أو إصدار أحدث (انظر دورة «Node.js»):
npm create vite@latest boutique -- --template react # يولّد المشروع
cd boutique
npm install # يحمّل react وreact-dom وvite…
npm run dev # خادم التطوير على http://localhost:5173
في كل مرة تحفظ فيها ملفًا، تتحدّث الصفحة دون إعادة تحميل (إعادة التحميل الساخن (hot reload)).
ما يحتويه المشروع
boutique/
├── package.json # التبعيات والسكربتات
├── index.html # صفحة HTML الوحيدة: div فارغ + سكربت الدخول
├── vite.config.js # إعدادات Vite (إضافة React)
├── public/ # ملفات تُنسخ كما هي (favicon وrobots.txt)
└── src/
├── main.jsx # نقطة الدخول: تركّب React داخل الـ div
├── App.jsx # المكوّن الجذر
└── index.css
package.json
{
"name": "boutique-artisanat",
"private": true,
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"dependencies": {
"react": "^19.1.0",
"react-dom": "^19.1.0"
},
"devDependencies": {
"@vitejs/plugin-react": "^5.0.0",
"vite": "^7.0.0"
}
}
- يحتوي
reactعلى المكوّنات وJSX والـ hooks؛ أماreact-domفيعرف كيف يعرض داخل المتصفح. - لا يُستعمل
viteوإضافته إلا للتطوير والبناء: لذلك فهما ضمنdevDependencies.
index.html
<!doctype html>
<html lang="fr">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Coopérative Tifawin</title>
</head>
<body>
<div id="root"></div> <!-- هنا سيعرض React كل شيء -->
<script type="module" src="/src/main.jsx"></script> <!-- يحوّل Vite شيفرة JSX -->
</body>
</html>
src/main.jsx
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import App from './App.jsx';
createRoot(document.getElementById('root')).render(
<StrictMode>
<App />
</StrictMode>
);
يتولّى createRoot التحكم في div#root؛ ويعرض فيه render المكوّن App. أما StrictMode فيفعّل فحوصات إضافية أثناء التطوير فقط (فهو مثلًا يستدعي بعض الشيفرات مرتين لكشف الأخطاء).
src/App.jsx
export default function App() {
return <h1>Coopérative Tifawin</h1>;
}
تهيئة جهاز العمل
- المحرّر: VS Code (أو ما يعادله) مع ESLint وPrettier.
- إضافة React Developer Tools في Chrome أو Firefox أو Edge: تضيف التبويبين Components وProfiler إلى أدوات المطوّر (الدرس 3.2).
- Node.js LTS وnpm محدّثان.
أخطاء متكررة
- اسم مكوّن بحرف صغير: يُعامَل
<carteProduit />كوسم HTML مجهول. يبدأ المكوّن دائمًا بحرف كبير. - فتح
index.htmlمباشرةً في المتصفح (بنقرة مزدوجة): لا تُحوَّل شيفرة JSX، فتبقى الصفحة بيضاء. استعملnpm run dev. - وضع
viteضمنdependencies: هذا يعمل، لكنه ليس ممارسة سليمة. أدوات البناء مكانهاdevDependencies. - الاعتقاد بأن React يعوّض JavaScript: كل ما تعلّمته (المصفوفات، الكائنات،
map،filter، الوحدات، الوعود (promises)) تحتاجه في كل سطر من React.
التطبيق العملي (Lab)
لا يتوفر الـ lab على شبكة: ستبني يدويًا الهيكل نفسه الذي كان سيولّده npm create vite، لمتجر التعاونية:
- الملف
package.jsonبوحدات ES؛ - السكربتات
devوbuildوpreview؛ - تبعيات React وVite (مصرَّح بها، غير مثبّتة)؛
- الصفحة
index.html؛ - نقطة الدخول
main.jsxوالمكوّنApp.jsx.
اختبار قصير
- ما معنى «العرض التصريحي»؟
- ماذا يفعل React خلال مرحلة العرض، وخلال مرحلة التثبيت؟
- لماذا يُعدّ
viteتبعيةً للتطوير؟ - ما فائدة العنصر
<div id="root">فيindex.html؟ - متى نفضّل Next.js على تطبيق Vite؟
الأجوبة
- نصف الواجهة المنتظرة لحالة معيّنة؛ ويحسب React بنفسه تغييرات الـ DOM ويطبّقها.
- العرض: يستدعي المكوّنات ويقارن النتيجة بالعرض السابق. التثبيت: يطبّق على الـ DOM الفروق فقط.
- لأنه لا يُستعمل إلا للتطوير ولبناء الملفات؛ والموقع في بيئة الإنتاج لا يحتاج إليه.
- إنه الحاوية التي يعرض فيها
createRoot(...).render(<App />)التطبيق بأكمله. - لموقع عمومي يحتاج إلى العرض من جهة الخادم (الظهور في محركات البحث، العرض الأول السريع) أو إلى مسارات من جهة الخادم؛ أما Vite فيناسب التطبيقات من جهة العميل التي تُقدَّم على شكل ملفات ثابتة.
المراجع: توثيق React بالفرنسية (fr.react.dev، رخصة CC BY 4.0). نص أصلي من MasterLinux.