استكشف أفضل الممارسات لاستخدام TypeScript مع React لبناء تطبيقات ويب قوية وقابلة للتوسع والصيانة. تعلم عن هيكلة المشاريع وتصميم المكونات والاختبار والتحسين.
TypeScript مع React: أفضل الممارسات للتطبيقات القابلة للتوسع والصيانة
يُعد TypeScript و React مزيجًا قويًا لبناء تطبيقات الويب الحديثة. يضيف TypeScript الأنواع الثابتة إلى JavaScript، مما يحسن جودة الكود وقابليته للصيانة، بينما يوفر React نهجًا تعبيريًا قائمًا على المكونات لبناء واجهات المستخدم. يستكشف هذا المقال أفضل الممارسات لاستخدام TypeScript مع React لإنشاء تطبيقات قوية وقابلة للتوسع والصيانة ومناسبة لجمهور عالمي.
لماذا نستخدم TypeScript مع React؟
قبل الخوض في أفضل الممارسات، دعنا نفهم لماذا يُعد TypeScript إضافة قيمة لتطوير React:
- تحسين جودة الكود: يساعد نظام الأنواع الثابتة في TypeScript على اكتشاف الأخطاء في وقت مبكر من عملية التطوير، مما يقلل من مشكلات وقت التشغيل ويحسن موثوقية الكود.
- تعزيز قابلية الصيانة: تجعل تعليقات الأنواع والواجهات (interfaces) الكود أسهل في الفهم وإعادة الهيكلة، مما يؤدي إلى قابلية صيانة أفضل على المدى الطويل.
- دعم أفضل لبيئة التطوير المتكاملة (IDE): يوفر TypeScript دعمًا ممتازًا لبيئات التطوير المتكاملة، بما في ذلك الإكمال التلقائي، والتنقل في الكود، وأدوات إعادة الهيكلة، مما يعزز إنتاجية المطورين.
- تقليل الأخطاء البرمجية: يلتقط نظام الأنواع الثابتة العديد من أخطاء JavaScript الشائعة قبل وقت التشغيل، مما يؤدي إلى تطبيق أكثر استقرارًا وخاليًا من الأخطاء.
- تحسين التعاون: تسهل تعريفات الأنواع الواضحة على الفرق التعاون في المشاريع الكبيرة، حيث يمكن للمطورين فهم الغرض من المكونات والدوال المختلفة واستخدامها بسرعة.
إعداد مشروع React باستخدام TypeScript
استخدام Create React App
أسهل طريقة لبدء مشروع React جديد باستخدام TypeScript هي عبر استخدام Create React App مع قالب TypeScript:
npx create-react-app my-typescript-react-app --template typescript
يقوم هذا الأمر بإعداد مشروع React أساسي مع تكوين TypeScript، بما في ذلك التبعيات الضرورية وملف tsconfig.json
.
إعداد ملف tsconfig.json
ملف tsconfig.json
هو قلب إعدادات TypeScript الخاصة بك. إليك بعض الإعدادات الموصى بها:
{
"compilerOptions": {
"target": "es5",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx"
},
"include": [
"src"
]
}
الخيارات الرئيسية التي يجب مراعاتها:
"strict": true
: يفعّل التدقيق الصارم للأنواع، وهو موصى به بشدة لاكتشاف الأخطاء المحتملة."esModuleInterop": true
: يتيح التشغيل البيني بين وحدات CommonJS و ES."jsx": "react-jsx"
: يفعّل تحويل JSX الجديد، مما يبسط كود React ويحسن الأداء.
أفضل الممارسات لمكونات React مع TypeScript
تحديد أنواع خصائص (Props) المكونات
أحد أهم جوانب استخدام TypeScript مع React هو تحديد أنواع خصائص مكوناتك بشكل صحيح. استخدم الواجهات (interfaces) أو псевдоніми типів (type aliases) لتعريف شكل كائن الخصائص.
interface MyComponentProps {
name: string;
age?: number; // خاصية اختيارية
onClick: () => void;
}
const MyComponent: React.FC = ({ name, age, onClick }) => {
return (
مرحباً، {name}!
{age && عمرك {age} سنة.
}
);
};
استخدام React.FC<MyComponentProps>
يضمن أن المكون هو مكون وظيفي وأن خصائصه محددة النوع بشكل صحيح.
تحديد أنواع حالة (State) المكونات
إذا كنت تستخدم المكونات الفئوية (class components)، فستحتاج أيضًا إلى تحديد نوع حالة المكون. عرّف واجهة أو псевдонім типу لحالة الكائن واستخدمها في تعريف المكون.
interface MyComponentState {
count: number;
}
class MyComponent extends React.Component<{}, MyComponentState> {
state: MyComponentState = {
count: 0
};
handleClick = () => {
this.setState({
count: this.state.count + 1
});
};
render() {
return (
العدد: {this.state.count}
);
}
}
بالنسبة للمكونات الوظيفية التي تستخدم خطاف useState
، يمكن لـ TypeScript غالبًا استنتاج نوع متغير الحالة، ولكن يمكنك أيضًا توفيره بشكل صريح:
import React, { useState } from 'react';
const MyComponent: React.FC = () => {
const [count, setCount] = useState(0);
return (
العدد: {count}
);
};
استخدام حراس الأنواع (Type Guards)
حراس الأنواع هي دوال تضيق نطاق نوع المتغير ضمن نطاق معين. تكون مفيدة عند التعامل مع أنواع الاتحاد (union types) أو عندما تحتاج إلى التأكد من أن المتغير له نوع معين قبل إجراء عملية ما.
interface Circle {
kind: "circle";
radius: number;
}
interface Square {
kind: "square";
side: number;
}
type Shape = Circle | Square;
function isCircle(shape: Shape): shape is Circle {
return shape.kind === "circle";
}
function getArea(shape: Shape): number {
if (isCircle(shape)) {
return Math.PI * shape.radius ** 2;
} else {
return shape.side ** 2;
}
}
الدالة isCircle
هي حارس نوع يتحقق مما إذا كان Shape
هو Circle
. داخل كتلة if
، يعرف TypeScript أن shape
هو Circle
ويسمح لك بالوصول إلى خاصية radius
الخاصة به.
التعامل مع الأحداث (Events)
عند التعامل مع الأحداث في React باستخدام TypeScript، من المهم تحديد نوع كائن الحدث بشكل صحيح. استخدم نوع الحدث المناسب من مساحة الأسماء React
.
const MyComponent: React.FC = () => {
const handleChange = (event: React.ChangeEvent) => {
console.log(event.target.value);
};
return (
);
};
في هذا المثال، يتم استخدام React.ChangeEvent<HTMLInputElement>
لتحديد نوع كائن الحدث لحدث التغيير على عنصر إدخال. هذا يوفر الوصول إلى خاصية target
، وهي HTMLInputElement
.
هيكلة المشروع
المشروع ذو الهيكلة الجيدة أمر حاسم لقابلية الصيانة والتوسع. إليك هيكل مشروع مقترح لتطبيق React باستخدام TypeScript:
src/
├── components/
│ ├── MyComponent/
│ │ ├── MyComponent.tsx
│ │ ├── MyComponent.module.css
│ │ └── index.ts
├── pages/
│ ├── HomePage.tsx
│ └── AboutPage.tsx
├── services/
│ ├── api.ts
│ └── auth.ts
├── types/
│ ├── index.ts
│ └── models.ts
├── utils/
│ ├── helpers.ts
│ └── constants.ts
├── App.tsx
├── index.tsx
├── react-app-env.d.ts
└── tsconfig.json
نقاط رئيسية:
- المكونات (Components): جمّع المكونات ذات الصلة في مجلدات. يجب أن يحتوي كل مجلد على ملف TypeScript للمكون، ووحدات CSS (إذا استخدمت)، وملف
index.ts
لتصدير المكون. - الصفحات (Pages): خزّن المكونات ذات المستوى الأعلى التي تمثل صفحات مختلفة من تطبيقك.
- الخدمات (Services): نفّذ استدعاءات API والخدمات الأخرى في هذا المجلد.
- الأنواع (Types): عرّف تعريفات الأنواع والواجهات العالمية في هذا المجلد.
- الأدوات المساعدة (Utils): خزّن الدوال المساعدة والثوابت.
- index.ts: استخدم ملفات
index.ts
لإعادة تصدير الوحدات من مجلد، مما يوفر واجهة برمجة تطبيقات نظيفة ومنظمة لاستيراد الوحدات.
استخدام الخطافات (Hooks) مع TypeScript
تسمح خطافات React باستخدام الحالة وميزات React الأخرى في المكونات الوظيفية. يعمل TypeScript بسلاسة مع الخطافات، مما يوفر أمان الأنواع ويحسن تجربة المطور.
useState
كما هو موضح سابقًا، يمكنك تحديد نوع متغير الحالة بشكل صريح عند استخدام useState
:
import React, { useState } from 'react';
const MyComponent: React.FC = () => {
const [count, setCount] = useState(0);
return (
العدد: {count}
);
};
useEffect
عند استخدام useEffect
، كن على دراية بمصفوفة التبعيات. يمكن لـ TypeScript مساعدتك في اكتشاف الأخطاء إذا نسيت تضمين تبعية مستخدمة داخل التأثير.
import React, { useState, useEffect } from 'react';
const MyComponent: React.FC = () => {
const [count, setCount] = useState(0);
useEffect(() => {
document.title = `العدد: ${count}`;
}, [count]); // أضف 'count' إلى مصفوفة التبعيات
return (
العدد: {count}
);
};
إذا حذفت count
من مصفوفة التبعيات، فسيتم تشغيل التأثير مرة واحدة فقط عند تحميل المكون، ولن يتم تحديث عنوان المستند عند تغيير العدد. سيحذرك TypeScript من هذه المشكلة المحتملة.
useContext
عند استخدام useContext
، تحتاج إلى توفير نوع لقيمة السياق.
import React, { createContext, useContext } from 'react';
interface ThemeContextType {
theme: string;
toggleTheme: () => void;
}
const ThemeContext = createContext(undefined);
const ThemeProvider: React.FC = ({ children }) => {
// نفذ منطق السمة هنا
return (
{} }}>
{children}
);
};
const MyComponent: React.FC = () => {
const { theme, toggleTheme } = useContext(ThemeContext) as ThemeContextType;
return (
السمة: {theme}
);
};
export { ThemeProvider, MyComponent };
من خلال توفير نوع لقيمة السياق، تضمن أن خطاف useContext
يعيد قيمة بالنوع الصحيح.
اختبار مكونات React المكتوبة بـ TypeScript
الاختبار جزء أساسي من بناء تطبيقات قوية. يعزز TypeScript الاختبار من خلال توفير أمان الأنواع وتحسين تغطية الكود.
الاختبار الوحدوي (Unit Testing)
استخدم أطر عمل الاختبار مثل Jest و React Testing Library لاختبار مكوناتك بشكل وحدوي.
// MyComponent.test.tsx
import React from 'react';
import { render, screen, fireEvent } from '@testing-library/react';
import MyComponent from './MyComponent';
describe('MyComponent', () => {
it('يعرض المكون بالاسم الصحيح', () => {
render( );
expect(screen.getByText('مرحباً، John!')).toBeInTheDocument();
});
it('يستدعي معالج onClick عند النقر على الزر', () => {
const onClick = jest.fn();
render( );
fireEvent.click(screen.getByRole('button'));
expect(onClick).toHaveBeenCalledTimes(1);
});
});
يساعد تدقيق الأنواع في TypeScript على اكتشاف الأخطاء في اختباراتك، مثل تمرير خصائص غير صحيحة أو استخدام معالجات أحداث خاطئة.
الاختبار التكاملي (Integration Testing)
تتحقق اختبارات التكامل من أن الأجزاء المختلفة من تطبيقك تعمل معًا بشكل صحيح. استخدم أدوات مثل Cypress أو Playwright للاختبار من البداية إلى النهاية.
تحسين الأداء
يمكن لـ TypeScript أيضًا المساعدة في تحسين الأداء عن طريق اكتشاف اختناقات الأداء المحتملة في وقت مبكر من عملية التطوير.
التخزين المؤقت (Memoization)
استخدم React.memo
لتخزين المكونات الوظيفية مؤقتًا ومنع إعادة العرض غير الضرورية.
import React from 'react';
interface MyComponentProps {
name: string;
}
const MyComponent: React.FC = ({ name }) => {
console.log('Rendering MyComponent');
return (
مرحباً، {name}!
);
};
export default React.memo(MyComponent);
سيقوم React.memo
بإعادة عرض المكون فقط إذا تغيرت خصائصه. يمكن أن يؤدي هذا إلى تحسين الأداء بشكل كبير، خاصة بالنسبة للمكونات المعقدة.
تقسيم الكود (Code Splitting)
استخدم الاستيراد الديناميكي لتقسيم الكود الخاص بك إلى أجزاء أصغر وتحميلها عند الطلب. يمكن أن يقلل هذا من وقت التحميل الأولي لتطبيقك.
import React, { Suspense } from 'react';
const MyComponent = React.lazy(() => import('./MyComponent'));
const App: React.FC = () => {
return (
جارٍ التحميل...
يسمح React.lazy
باستيراد المكونات ديناميكيًا، والتي يتم تحميلها فقط عند الحاجة إليها. يوفر المكون Suspense
واجهة مستخدم بديلة أثناء تحميل المكون.
الخاتمة
يمكن أن يؤدي استخدام TypeScript مع React إلى تحسين جودة تطبيقات الويب وقابليتها للصيانة والتوسع بشكل كبير. باتباع هذه الممارسات الأفضل، يمكنك الاستفادة من قوة TypeScript لبناء تطبيقات قوية وعالية الأداء تلبي احتياجات جمهور عالمي. تذكر التركيز على تعريفات الأنواع الواضحة، وتنظيم المشروع بشكل جيد، والاختبار الشامل لضمان نجاح مشاريعك على المدى الطويل.