مقدمة إلى رياكت (React)
رياكت (React) هي مكتبة جافاسكريبت مفتوحة المصدر تم تطويرها بواسطة فيسبوك (الآن ميتا) في عام 2013، وهي مخصصة لبناء واجهات المستخدم التفاعلية للتطبيقات أحادية الصفحة (Single Page Applications). تتميز رياكت بنهجها القائم على المكونات (Components) وكفاءتها العالية في تحديث وعرض البيانات.
في هذا المقال الشامل، سنتعرف على كل ما يتعلق برياكت من المفاهيم الأساسية إلى التقنيات المتقدمة، مع توضيح كل مفهوم بأمثلة عملية.
لماذا رياكت؟
قبل الغوص في تفاصيل رياكت، دعونا نفهم لماذا أصبحت هذه المكتبة خيارًا شائعًا للمطورين:
مزايا استخدام رياكت
- نهج المكونات (Component-Based): يسمح بإنشاء واجهات معقدة من خلال مكونات صغيرة ومعزولة يمكن إعادة استخدامها.
- الـ Virtual DOM: يحسن الأداء من خلال تحديث أجزاء محددة من الـ DOM الحقيقي فقط عند الحاجة.
- تدفق البيانات أحادي الاتجاه (One-Way Data Flow): يجعل تتبع التغييرات وتصحيح الأخطاء أسهل.
- JSX: يسمح بكتابة هياكل HTML-like داخل جافاسكريبت، مما يسهل فهم وكتابة المكونات.
- بيئة نظام بيئي غنية: مجموعة واسعة من المكتبات والأدوات المساعدة.
- دعم مجتمعي قوي: مجتمع نشط ووثائق ممتازة.
البدء مع رياكت
متطلبات ما قبل البدء
قبل البدء في استخدام رياكت، يجب أن يكون لديك:
- معرفة أساسية بـ HTML، CSS، وجافاسكريبت
- Node.js و npm (أو yarn) مثبتة على جهازك
إنشاء تطبيق رياكت جديد
هناك عدة طرق لإنشاء تطبيق رياكت، لكن الطريقة الأكثر شيوعًا هي استخدام Create React App:
npx create-react-app my-app
cd my-app
npm start
هذا سينشئ تطبيق رياكت جديد ويشغله على المنفذ 3000 (http://localhost:3000).
المفاهيم الأساسية في رياكت
المكونات (Components)
المكونات هي اللبنات الأساسية لتطبيقات رياكت. يمكن تقسيمها إلى نوعين:
1. مكونات الفئة (Class Components)
import React, { Component } from 'react';
class Welcome extends Component {
render() {
return <h1>مرحبا، {this.props.name}</h1>;
}
}
export default Welcome;
2. مكونات وظيفية (Functional Components)
import React from 'react';
function Welcome(props) {
return <h1>مرحبا، {props.name}</h1>;
}
export default Welcome;
JSX
JSX هو امتداد لجافاسكريبت يسمح بكتابة هياكل شبيهة بـ HTML داخل جافاسكريبت:
const element = <h1>مرحبا بالعالم!</h1>;
يتم تحويل JSX إلى استدعاءات React.createElement()
أثناء عملية البناء:
const element = React.createElement(
'h1',
null,
'مرحبا بالعالم!'
);
الخصائص (Props)
الخصائص (Props) هي الطريقة التي تمرر بها البيانات من المكون الأب إلى المكون الابن:
// المكون الأب
function App() {
return <Welcome name="أحمد" />;
}
// المكون الابن
function Welcome(props) {
return <h1>مرحبا، {props.name}</h1>;
}
الحالة (State)
الحالة (State) هي بيانات خاصة بالمكون يمكن أن تتغير مع مرور الوقت:
في مكونات الفئة:
class Counter extends React.Component {
constructor(props) {
super(props);
this.state = { count: 0 };
}
increment = () => {
this.setState({ count: this.state.count + 1 });
}
render() {
return (
<div>
<p>العدد: {this.state.count}</p>
<button onClick={this.increment}>زيادة</button>
</div>
);
}
}
في المكونات الوظيفية (باستخدام Hooks):
import React, { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>العدد: {count}</p>
<button onClick={() => setCount(count + 1)}>زيادة</button>
</div>
);
}
دورة حياة المكون (Component Lifecycle)
في مكونات الفئة، هناك عدة طرق لدورة الحياة تُستدعى في مراحل مختلفة:
class LifecycleDemo extends React.Component {
constructor(props) {
super(props);
this.state = { data: null };
console.log('1. Constructor');
}
componentDidMount() {
console.log('3. componentDidMount');
// مثالي لجلب البيانات
fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => this.setState({ data }));
}
componentDidUpdate(prevProps, prevState) {
console.log('4. componentDidUpdate');
// يُستدعى بعد تحديث المكون
}
componentWillUnmount() {
console.log('5. componentWillUnmount');
// تنظيف (مثل إلغاء الاشتراكات)
}
render() {
console.log('2. render');
return <div>مثال على دورة الحياة</div>;
}
}
في المكونات الوظيفية، يمكن استخدام useEffect
لمحاكاة طرق دورة الحياة:
import React, { useState, useEffect } from 'react';
function LifecycleDemo() {
const [data, setData] = useState(null);
// يعادل componentDidMount و componentDidUpdate
useEffect(() => {
console.log('المكون تم تحميله أو تحديثه');
// جلب البيانات
fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => setData(data));
// يعادل componentWillUnmount
return () => {
console.log('المكون سيتم إزالته');
// تنظيف
};
}, []); // المصفوفة الفارغة تعني أن هذا التأثير سيعمل فقط بعد التحميل الأولي
return <div>مثال على دورة الحياة</div>;
}
الخطافات (Hooks)
الخطافات (Hooks) هي ميزة أُضيفت في رياكت 16.8 تسمح باستخدام الحالة وميزات رياكت الأخرى دون كتابة مكون فئة.
useState
يسمح بإضافة حالة محلية للمكونات الوظيفية:
import React, { useState } from 'react';
function Example() {
// إعلان متغير حالة جديد يسمى "count"
const [count, setCount] = useState(0);
return (
<div>
<p>لقد ضغطت على الزر {count} مرات</p>
<button onClick={() => setCount(count + 1)}>
انقر هنا
</button>
</div>
);
}
useEffect
يسمح بتنفيذ “آثار جانبية” في المكونات الوظيفية:
import React, { useState, useEffect } from 'react';
function Example() {
const [count, setCount] = useState(0);
// مشابه لـ componentDidMount و componentDidUpdate:
useEffect(() => {
// تحديث عنوان الصفحة باستخدام API المتصفح
document.title = `لقد ضغطت ${count} مرات`;
});
return (
<div>
<p>لقد ضغطت على الزر {count} مرات</p>
<button onClick={() => setCount(count + 1)}>
انقر هنا
</button>
</div>
);
}
useContext
يسمح بالوصول إلى السياق (Context) دون استخدام Consumer:
import React, { useContext } from 'react';
// إنشاء سياق
const ThemeContext = React.createContext('light');
function ThemedButton() {
// استخدام السياق
const theme = useContext(ThemeContext);
return <button className={theme}>زر بسمة</button>;
}
function App() {
return (
<ThemeContext.Provider value="dark">
<ThemedButton />
</ThemeContext.Provider>
);
}
useReducer
بديل لـ useState عندما تكون لديك منطق حالة معقد:
import React, { useReducer } from 'react';
// الدالة المخفضة (reducer)
function reducer(state, action) {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1 };
default:
throw new Error();
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, { count: 0 });
return (
<div>
العدد: {state.count}
<button onClick={() => dispatch({ type: 'increment' })}>+</button>
<button onClick={() => dispatch({ type: 'decrement' })}>-</button>
</div>
);
}
خطافات مخصصة (Custom Hooks)
يمكنك إنشاء خطافات مخصصة لاستخراج منطق المكون إلى وظائف قابلة لإعادة الاستخدام:
import { useState, useEffect } from 'react';
// خطاف مخصص لجلب البيانات
function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchData = async () => {
try {
setLoading(true);
const response = await fetch(url);
if (!response.ok) {
throw new Error(`خطأ في الاتصال: ${response.status}`);
}
const result = await response.json();
setData(result);
setError(null);
} catch (error) {
setError(error.message);
} finally {
setLoading(false);
}
};
fetchData();
}, [url]);
return { data, loading, error };
}
// استخدام الخطاف المخصص
function DataComponent() {
const { data, loading, error } = useFetch('https://api.example.com/data');
if (loading) return <div>جاري التحميل...</div>;
if (error) return <div>خطأ: {error}</div>;
return (
<div>
<h2>البيانات:</h2>
<pre>{JSON.stringify(data, null, 2)}</pre>
</div>
);
}
التوجيه في رياكت (React Router)
React Router هي مكتبة شائعة للتعامل مع التوجيه في تطبيقات رياكت أحادية الصفحة (SPA).
التثبيت
npm install react-router-dom
الاستخدام الأساسي
import React from 'react';
import { BrowserRouter, Routes, Route, Link } from 'react-router-dom';
// مكونات الصفحات
function Home() {
return <h2>الصفحة الرئيسية</h2>;
}
function About() {
return <h2>من نحن</h2>;
}
function Contact() {
return <h2>اتصل بنا</h2>;
}
// مكون التطبيق الرئيسي
function App() {
return (
<BrowserRouter>
<div>
<nav>
<ul>
<li>
<Link to="/">الرئيسية</Link>
</li>
<li>
<Link to="/about">من نحن</Link>
</li>
<li>
<Link to="/contact">اتصل بنا</Link>
</li>
</ul>
</nav>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/contact" element={<Contact />} />
</Routes>
</div>
</BrowserRouter>
);
}
المعلمات في الروابط (URL Parameters)
import { BrowserRouter, Routes, Route, Link, useParams } from 'react-router-dom';
function User() {
// استخراج معلمة userId من الرابط
const { userId } = useParams();
return <h2>ملف المستخدم: {userId}</h2>;
}
function App() {
return (
<BrowserRouter>
<div>
<nav>
<ul>
<li>
<Link to="/users/1">المستخدم 1</Link>
</li>
<li>
<Link to="/users/2">المستخدم 2</Link>
</li>
</ul>
</nav>
<Routes>
<Route path="/users/:userId" element={<User />} />
</Routes>
</div>
</BrowserRouter>
);
}
التوجيه المتداخل (Nested Routing)
import { BrowserRouter, Routes, Route, Link, Outlet } from 'react-router-dom';
function Dashboard() {
return (
<div>
<h2>لوحة التحكم</h2>
<nav>
<Link to="profile">الملف الشخصي</Link> |
<Link to="settings">الإعدادات</Link>
</nav>
<Outlet /> {/* هنا سيتم عرض المكونات المتداخلة */}
</div>
);
}
function Profile() {
return <h3>الملف الشخصي</h3>;
}
function Settings() {
return <h3>الإعدادات</h3>;
}
function App() {
return (
<BrowserRouter>
<Routes>
<Route path="dashboard" element={<Dashboard />}>
<Route path="profile" element={<Profile />} />
<Route path="settings" element={<Settings />} />
</Route>
</Routes>
</BrowserRouter>
);
}
إدارة الحالة في رياكت
Context API
Context API هي ميزة مدمجة في رياكت تسمح بمشاركة البيانات بين المكونات دون الحاجة إلى تمرير الخصائص (props) يدويًا عبر كل مستوى.
import React, { createContext, useContext, useState } from 'react';
// إنشاء السياق
const UserContext = createContext();
// مزود السياق (Provider)
function UserProvider({ children }) {
const [user, setUser] = useState({ name: 'أحمد', isLoggedIn: true });
return (
<UserContext.Provider value={{ user, setUser }}>
{children}
</UserContext.Provider>
);
}
// خطاف مخصص لاستخدام السياق
function useUser() {
const context = useContext(UserContext);
if (context === undefined) {
throw new Error('useUser يجب أن يستخدم داخل UserProvider');
}
return context;
}
// مكون يستخدم السياق
function UserProfile() {
const { user, setUser } = useUser();
return (
<div>
<h2>الملف الشخصي</h2>
<p>اسم المستخدم: {user.name}</p>
<p>حالة الدخول: {user.isLoggedIn ? 'متصل' : 'غير متصل'}</p>
<button
onClick={() =>
setUser({ ...user, isLoggedIn: !user.isLoggedIn })
}
>
{user.isLoggedIn ? 'تسجيل الخروج' : 'تسجيل الدخول'}
</button>
</div>
);
}
// التطبيق الرئيسي
function App() {
return (
<UserProvider>
<div>
<h1>تطبيق المستخدمين</h1>
<UserProfile />
</div>
</UserProvider>
);
}
Redux
Redux هي مكتبة شائعة لإدارة الحالة في تطبيقات جافاسكريبت، وتستخدم بشكل شائع مع رياكت.
التثبيت
npm install redux react-redux @reduxjs/toolkit
مثال بسيط باستخدام Redux Toolkit
import React from 'react';
import { configureStore, createSlice } from '@reduxjs/toolkit';
import { Provider, useSelector, useDispatch } from 'react-redux';
// إنشاء شريحة (slice) للعداد
const counterSlice = createSlice({
name: 'counter',
initialState: {
value: 0,
},
reducers: {
increment: (state) => {
state.value += 1;
},
decrement: (state) => {
state.value -= 1;
},
incrementByAmount: (state, action) => {
state.value += action.payload;
},
},
});
// تصدير الإجراءات (actions)
export const { increment, decrement, incrementByAmount } = counterSlice.actions;
// إنشاء المتجر (store)
const store = configureStore({
reducer: {
counter: counterSlice.reducer,
},
});
// مكون العداد
function Counter() {
const count = useSelector((state) => state.counter.value);
const dispatch = useDispatch();
return (
<div>
<h2>العداد: {count}</h2>
<button onClick={() => dispatch(increment())}>زيادة</button>
<button onClick={() => dispatch(decrement())}>نقصان</button>
<button onClick={() => dispatch(incrementByAmount(5))}>زيادة بمقدار 5</button>
</div>
);
}
// التطبيق الرئيسي
function App() {
return (
<Provider store={store}>
<div>
<h1>مثال على Redux</h1>
<Counter />
</div>
</Provider>
);
}
تحسين الأداء في رياكت
React.memo
React.memo
هو HOC (مكون عالي الترتيب) يقوم بتخزين نتائج عملية التصيير (rendering) للمكون ويتجنب إعادة التصيير إذا لم تتغير الخصائص (props).
import React from 'react';
function ExpensiveComponent({ name }) {
console.log('تصيير المكون المكلف');
return <div>مرحبا، {name}</div>;
}
// تحسين المكون باستخدام React.memo
const MemoizedExpensiveComponent = React.memo(ExpensiveComponent);
function App() {
const [count, setCount] = useState(0);
const [name, setName] = useState('أحمد');
return (
<div>
<h1>العداد: {count}</h1>
<button onClick={() => setCount(count + 1)}>زيادة</button>
<input
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="أدخل اسمًا"
/>
<MemoizedExpensiveComponent name={name} />
</div>
);
}
useCallback
useCallback
هو خطاف يقوم بتخزين دالة بين عمليات إعادة التصيير.
import React, { useState, useCallback } from 'react';
function ChildComponent({ onClick }) {
console.log('تصيير المكون الابن');
return <button onClick={onClick}>انقر هنا</button>;
}
const MemoizedChild = React.memo(ChildComponent);
function ParentComponent() {
const [count, setCount] = useState(0);
// بدون useCallback، سيتم إنشاء دالة جديدة في كل مرة يتم فيها تصيير المكون الأب
// const handleClick = () => {
// console.log('تم النقر!');
// };
// مع useCallback، يتم تخزين الدالة وإعادة استخدامها
const handleClick = useCallback(() => {
console.log('تم النقر!');
}, []);
return (
<div>
<h2>العداد: {count}</h2>
<button onClick={() => setCount(count + 1)}>زيادة</button>
<MemoizedChild onClick={handleClick} />
</div>
);
}
useMemo
useMemo
هو خطاف يقوم بتخزين نتيجة حساب مكلف.
import React, { useState, useMemo } from 'react';
function ExpensiveCalculation({ list }) {
// حساب مكلف
const total = useMemo(() => {
console.log('حساب المجموع...');
return list.reduce((acc, item) => acc + item, 0);
}, [list]); // إعادة الحساب فقط عندما تتغير القائمة
return <div>المجموع: {total}</div>;
}
function App() {
const [count, setCount] = useState(0);
const [list, setList] = useState([1, 2, 3, 4, 5]);
const addToList = () => {
setList([...list, Math.floor(Math.random() * 100)]);
};
return (
<div>
<h2>العداد: {count}</h2>
<button onClick={() => setCount(count + 1)}>زيادة</button>
<button onClick={addToList}>إضافة رقم عشوائي</button>
<ExpensiveCalculation list={list} />
</div>
);
}
أفضل الممارسات في رياكت
1. تقسيم المكونات
قم بتقسيم المكونات الكبيرة إلى مكونات أصغر وأكثر قابلية لإعادة الاستخدام:
// بدلاً من هذا
function UserDashboard() {
return (
<div>
<header>
<h1>لوحة تحكم المستخدم</h1>
<nav>{/* ... */}</nav>
</header>
<main>
<section>{/* معلومات المستخدم */}</section>
<section>{/* إحصائيات */}</section>
<section>{/* قائمة المهام */}</section>
</main>
<footer>{/* ... */}</footer>
</div>
);
}
// استخدم هذا
function Header() {
return (
<header>
<h1>لوحة تحكم المستخدم</h1>
<Navigation />
</header>
);
}
function UserInfo() {
return <section>{/* معلومات المستخدم */}</section>;
}
function Statistics() {
return <section>{/* إحصائيات */}</section>;
}
function TaskList() {
return <section>{/* قائمة المهام */}</section>;
}
function Footer() {
return <footer>{/* ... */}</footer>;
}
function UserDashboard() {
return (
<div>
<Header />
<main>
<UserInfo />
<Statistics />
<TaskList />
</main>
<Footer />
</div>
);
}
2. استخدام المكونات الوظيفية والخطافات
يُفضل استخدام المكونات الوظيفية والخطافات بدلاً من مكونات الفئة، لأنها:
- أبسط وأسهل في الفهم
- تقلل من حجم الكود
- تسهل اختبار المكونات
- تحسن الأداء في بعض الحالات
3. استخدام TypeScript
استخدام TypeScript يوفر فوائد عديدة:
- اكتشاف الأخطاء أثناء التطوير
- توثيق أفضل للكود
- تحسين تجربة التطوير مع اقتراحات أفضل
// مثال على مكون بـ TypeScript
interface UserProps {
name: string;
age: number;
isAdmin?: boolean;
}
function User({ name, age, isAdmin = false }: UserProps) {
return (
<div>
<h2>{name}</h2>
<p>العمر: {age}</p>
{isAdmin && <p>مدير</p>}
</div>
);
}
4. استخدام الأنماط المناسبة لإدارة الحالة
اختر الأسلوب المناسب لإدارة الحالة بناءً على حجم وتعقيد التطبيق:
- للتطبيقات الصغيرة: استخدم
useState
وuseReducer
- للتطبيقات متوسطة الحجم: استخدم Context API
- للتطبيقات الكبيرة والمعقدة: استخدم Redux أو Zustand أو Recoil
5. تجنب إعادة التصيير غير الضرورية
استخدم أدوات تحسين الأداء مثل React.memo
، useCallback
، و useMemo
لتجنب إعادة التصيير غير الضرورية.
6. استخدام الكسول للمكونات (Lazy Loading)
استخدم React.lazy
و Suspense
لتحميل المكونات بشكل كسول:
import React, { Suspense, lazy } from 'react';
// تحميل المكون بشكل كسول
const HeavyComponent = lazy(() => import('./HeavyComponent'));
function App() {
return (
<div>
<Suspense fallback={<div>جاري التحميل...</div>}>
<HeavyComponent />
</Suspense>
</div>
);
}
ميزات رياكت الحديثة (2023-2024)
1. React Server Components
مكونات الخادم في رياكت تسمح بتشغيل مكونات على الخادم، مما يحسن الأداء ويقلل من حجم JavaScript المرسل إلى المتصفح.
2. React Suspense للبيانات
تحسينات على Suspense تسمح بتعليق عرض المكون حتى يتم تحميل البيانات:
function ProfilePage() {
return (
<Suspense fallback={<Spinner />}>
<ProfileDetails />
</Suspense>
);
}
3. Concurrent Mode
وضع التزامن يسمح لرياكت بمقاطعة عمليات التصيير ذات الأولوية المنخفضة لصالح التحديثات ذات الأولوية الأعلى.
4. Automatic Batching
تجميع تحديثات الحالة تلقائيًا لتحسين الأداء.
5. تحسينات على useEffect
تحسينات على خطاف useEffect
لتقليل الآثار الجانبية غير المرغوب فيها.
أدوات وإضافات مفيدة لرياكت
1. Next.js
إطار عمل يبني على رياكت ويوفر ميزات مثل التصيير على الخادم (SSR) والتوليد الثابت (SSG).
2. React Query
مكتبة لإدارة حالة البيانات وجلبها من الخادم:
import { useQuery } from 'react-query';
function Users() {
const { data, isLoading, error } = useQuery('users', fetchUsers);
if (isLoading) return <div>جاري التحميل...</div>;
if (error) return <div>حدث خطأ: {error.message}</div>;
return (
<ul>
{data.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
3. Styled Components
مكتبة لكتابة CSS في JavaScript:
import styled from 'styled-components';
const Button = styled.button`
background-color: #3498db;
color: white;
padding: 10px 15px;
border: none;
border-radius: 4px;
cursor: pointer;
&:hover {
background-color: #2980b9;
}
`;
function App() {
return <Button>انقر هنا</Button>;
}
4. React Testing Library
مكتبة لاختبار مكونات رياكت:
import { render, screen, fireEvent } from '@testing-library/react';
import Counter from './Counter';
test('يزيد العداد عند النقر على الزر', () => {
render(<Counter />);
const button = screen.getByText('زيادة');
const counter = screen.getByText('العدد: 0');
fireEvent.click(button);
expect(screen.getByText('العدد: 1')).toBeInTheDocument();
});
الخلاصة
رياكت هي مكتبة قوية ومرنة لبناء واجهات المستخدم التفاعلية. تتميز بنهجها القائم على المكونات، وكفاءتها في تحديث DOM، ونظام تدفق البيانات أحادي الاتجاه.
في هذا المقال، تعرفنا على:
- المفاهيم الأساسية في رياكت (المكونات، JSX، الخصائص، الحالة)
- دورة حياة المكونات
- الخطافات (Hooks) وكيفية استخدامها
- التوجيه في رياكت
- إدارة الحالة (Context API و Redux)
- تحسين الأداء
- أفضل الممارسات
- الميزات الحديثة والأدوات المفيدة
لمزيد من المعلومات، يمكنك زيارة الموقع الرسمي لرياكت والاطلاع على التوثيق الرسمي.
هل لديك أسئلة حول رياكت؟ شاركنا في التعليقات!