العربية

استكشف أفضل الممارسات لاستخدام TypeScript مع React لبناء تطبيقات ويب قوية وقابلة للتوسع والصيانة. تعلم عن هيكلة المشاريع وتصميم المكونات والاختبار والتحسين.

TypeScript مع React: أفضل الممارسات للتطبيقات القابلة للتوسع والصيانة

يُعد TypeScript و React مزيجًا قويًا لبناء تطبيقات الويب الحديثة. يضيف TypeScript الأنواع الثابتة إلى JavaScript، مما يحسن جودة الكود وقابليته للصيانة، بينما يوفر React نهجًا تعبيريًا قائمًا على المكونات لبناء واجهات المستخدم. يستكشف هذا المقال أفضل الممارسات لاستخدام TypeScript مع React لإنشاء تطبيقات قوية وقابلة للتوسع والصيانة ومناسبة لجمهور عالمي.

لماذا نستخدم TypeScript مع React؟

قبل الخوض في أفضل الممارسات، دعنا نفهم لماذا يُعد TypeScript إضافة قيمة لتطوير React:

إعداد مشروع 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"
  ]
}

الخيارات الرئيسية التي يجب مراعاتها:

أفضل الممارسات لمكونات 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

نقاط رئيسية:

استخدام الخطافات (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 لبناء تطبيقات قوية وعالية الأداء تلبي احتياجات جمهور عالمي. تذكر التركيز على تعريفات الأنواع الواضحة، وتنظيم المشروع بشكل جيد، والاختبار الشامل لضمان نجاح مشاريعك على المدى الطويل.

مصادر إضافية