تطوير تطبيقات React.js المتقدمة مع TypeScript و Next.js
React.js, TypeScript, Next.js
Linux
متقدم
15219 مشاهدة
2025/12/07
دليل شامل لتطوير تطبيقات React.js المتقدمة مع TypeScript و Next.js
المقدمة
React.js هو أحد أشهر مكتبات JavaScript لبناء واجهات المستخدم، ومع إضافة TypeScript و Next.js يصبح قوياً بشكل لا يُقاوم. في هذا الدليل المفصل جداً، سنغطي كل ما تحتاجه لبناء تطبيقات React.js متقدمة مع شرح كل مفهوم والمنطق وراءه.
ما سنتعلم:
- أساسيات TypeScript مع React
- Next.js و Server-Side Rendering
- إدارة الحالة المتقدمة
- Testing مع Jest و React Testing Library
- أدوات التطوير والتحسين
- NPM Scripts والأتمتة
- نشر التطبيقات
المتطلبات الأساسية:
- معرفة جيدة بـ JavaScript (ES6+)
- فهم أساسيات React.js
- Node.js مثبت على النظام
- محرر كود جيد (VS Code موصى به)
الخطوة 1: إعداد مشروع Next.js مع TypeScript
إنشاء مشروع جديد:
npx create-next-app@latest my-app --typescript --tailwind --eslint --app
cd my-app
فهم هيكل المشروع:
my-app/
├── app/ # App Router (Next.js 13+)
│ ├── layout.tsx # Root layout
│ ├── page.tsx # Home page
│ └── globals.css # Global styles
├── components/ # Reusable components
├── lib/ # Utility functions
├── public/ # Static assets
├── types/ # TypeScript type definitions
├── utils/ # Helper functions
├── package.json
├── next.config.js
├── tailwind.config.js
└── tsconfig.json
إعداد TypeScript:
// tsconfig.json
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "es6"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"plugins": [
{
"name": "next"
}
],
"baseUrl": ".",
"paths": {
"@/*": ["./*"],
"@/components/*": ["./components/*"],
"@/lib/*": ["./lib/*"],
"@/types/*": ["./types/*"],
"@/utils/*": ["./utils/*"]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
}
الخطوة 2: إنشاء أنواع البيانات (Types)
إنشاء أنواع أساسية:
// types/index.ts
export interface User {
id: string;
name: string;
email: string;
avatar?: string;
createdAt: Date;
updatedAt: Date;
}
export interface Post {
id: string;
title: string;
content: string;
author: User;
tags: string[];
published: boolean;
createdAt: Date;
updatedAt: Date;
}
export interface Comment {
id: string;
content: string;
author: User;
postId: string;
createdAt: Date;
}
export interface ApiResponse {
data: T;
message?: string;
success: boolean;
}
export interface PaginatedResponse {
data: T[];
pagination: {
page: number;
limit: number;
total: number;
totalPages: number;
};
}
أنواع React المخصصة:
// types/react.ts
import { ReactNode, ComponentProps } from 'react';
// Extended component props
export type ComponentPropsWithChildren = T & {
children?: ReactNode;
};
// Button variants
export type ButtonVariant = 'primary' | 'secondary' | 'danger' | 'success';
export type ButtonSize = 'sm' | 'md' | 'lg';
// Form field types
export interface FormFieldProps {
name: string;
label?: string;
error?: string;
required?: boolean;
disabled?: boolean;
}
// API hook types
export interface UseQueryOptions {
enabled?: boolean;
refetchOnWindowFocus?: boolean;
staleTime?: number;
}
export interface UseMutationOptions {
onSuccess?: (data: TData) => void;
onError?: (error: Error) => void;
onSettled?: () => void;
}
الخطوة 3: إنشاء Hooks مخصصة
Hook للـ API calls:
// lib/hooks/useApi.ts
import { useState, useEffect, useCallback } from 'react';
import { ApiResponse } from '@/types';
interface UseApiOptions {
immediate?: boolean;
}
export function useApi(
apiCall: () => Promise>,
options: UseApiOptions = {}
) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const execute = useCallback(async () => {
try {
setLoading(true);
setError(null);
const response = await apiCall();
if (response.success) {
setData(response.data);
} else {
setError(response.message || 'An error occurred');
}
} catch (err) {
setError(err instanceof Error ? err.message : 'An error occurred');
} finally {
setLoading(false);
}
}, [apiCall]);
useEffect(() => {
if (options.immediate) {
execute();
}
}, [execute, options.immediate]);
return { data, loading, error, execute, refetch: execute };
}
Hook للـ Local Storage:
// lib/hooks/useLocalStorage.ts
import { useState, useEffect } from 'react';
export function useLocalStorage(
key: string,
initialValue: T
): [T, (value: T | ((val: T) => T)) => void] {
const [storedValue, setStoredValue] = useState(() => {
if (typeof window === 'undefined') {
return initialValue;
}
try {
const item = window.localStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
} catch (error) {
console.error(`Error reading localStorage key "${key}":`, error);
return initialValue;
}
});
const setValue = (value: T | ((val: T) => T)) => {
try {
const valueToStore = value instanceof Function ? value(storedValue) : value;
setStoredValue(valueToStore);
if (typeof window !== 'undefined') {
window.localStorage.setItem(key, JSON.stringify(valueToStore));
}
} catch (error) {
console.error(`Error setting localStorage key "${key}":`, error);
}
};
return [storedValue, setValue];
}
الخطوة 4: إنشاء مكونات أساسية (Base Components)
مكون Button:
// components/ui/Button.tsx
import { forwardRef } from 'react';
import { cva, type VariantProps } from 'class-variance-authority';
import { cn } from '@/lib/utils';
const buttonVariants = cva(
'inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none ring-offset-background',
{
variants: {
variant: {
default: 'bg-primary text-primary-foreground hover:bg-primary/90',
destructive: 'bg-destructive text-destructive-foreground hover:bg-destructive/90',
outline: 'border border-input hover:bg-accent hover:text-accent-foreground',
secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80',
ghost: 'hover:bg-accent hover:text-accent-foreground',
link: 'underline-offset-4 hover:underline text-primary',
},
size: {
default: 'h-10 py-2 px-4',
sm: 'h-9 px-3 rounded-md',
lg: 'h-11 px-8 rounded-md',
icon: 'h-10 w-10',
},
},
defaultVariants: {
variant: 'default',
size: 'default',
},
}
);
export interface ButtonProps
extends React.ButtonHTMLAttributes,
VariantProps {
asChild?: boolean;
}
const Button = forwardRef(
({ className, variant, size, asChild = false, ...props }, ref) => {
return (
);
}
);
Button.displayName = 'Button';
export { Button, buttonVariants };
مكون Input:
// components/ui/Input.tsx
import * as React from 'react';
import { cn } from '@/lib/utils';
export interface InputProps
extends React.InputHTMLAttributes {}
const Input = React.forwardRef(
({ className, type, ...props }, ref) => {
return (
);
}
);
Input.displayName = 'Input';
export { Input };
الخطوة 5: إعداد إدارة الحالة مع Zustand
تثبيت Zustand:
npm install zustand
إنشاء store للمستخدم:
// lib/stores/userStore.ts
import { create } from 'zustand';
import { devtools, persist } from 'zustand/middleware';
import { User } from '@/types';
interface UserState {
user: User | null;
isAuthenticated: boolean;
login: (user: User) => void;
logout: () => void;
updateUser: (updates: Partial) => void;
}
export const useUserStore = create()(
devtools(
persist(
(set, get) => ({
user: null,
isAuthenticated: false,
login: (user: User) => {
set({ user, isAuthenticated: true });
},
logout: () => {
set({ user: null, isAuthenticated: false });
},
updateUser: (updates: Partial) => {
const currentUser = get().user;
if (currentUser) {
set({ user: { ...currentUser, ...updates } });
}
},
}),
{
name: 'user-storage',
}
),
{
name: 'user-store',
}
)
);
Store للإعدادات:
// lib/stores/settingsStore.ts
import { create } from 'zustand';
import { devtools, persist } from 'zustand/middleware';
interface SettingsState {
theme: 'light' | 'dark' | 'system';
language: string;
notifications: boolean;
setTheme: (theme: 'light' | 'dark' | 'system') => void;
setLanguage: (language: string) => void;
toggleNotifications: () => void;
}
export const useSettingsStore = create()(
devtools(
persist(
(set) => ({
theme: 'system',
language: 'ar',
notifications: true,
setTheme: (theme) => set({ theme }),
setLanguage: (language) => set({ language }),
toggleNotifications: () => set((state) => ({ notifications: !state.notifications })),
}),
{
name: 'settings-storage',
}
),
{
name: 'settings-store',
}
)
);
الخطوة 6: إعداد API Routes
إنشاء API route للمستخدمين:
// app/api/users/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { z } from 'zod';
const createUserSchema = z.object({
name: z.string().min(2).max(50),
email: z.string().email(),
password: z.string().min(8),
});
export async function GET(request: NextRequest) {
try {
// Get users from database
const users = await getUsers();
return NextResponse.json({
data: users,
success: true,
});
} catch (error) {
return NextResponse.json(
{ error: 'Failed to fetch users', success: false },
{ status: 500 }
);
}
}
export async function POST(request: NextRequest) {
try {
const body = await request.json();
const validatedData = createUserSchema.parse(body);
// Create user in database
const user = await createUser(validatedData);
return NextResponse.json(
{
data: user,
message: 'User created successfully',
success: true,
},
{ status: 201 }
);
} catch (error) {
if (error instanceof z.ZodError) {
return NextResponse.json(
{ error: error.errors, success: false },
{ status: 400 }
);
}
return NextResponse.json(
{ error: 'Failed to create user', success: false },
{ status: 500 }
);
}
}
API route لمنشور واحد:
// app/api/users/[id]/route.ts
import { NextRequest, NextResponse } from 'next/server';
interface RouteParams {
params: {
id: string;
};
}
export async function GET(
request: NextRequest,
{ params }: RouteParams
) {
try {
const user = await getUserById(params.id);
if (!user) {
return NextResponse.json(
{ error: 'User not found', success: false },
{ status: 404 }
);
}
return NextResponse.json({
data: user,
success: true,
});
} catch (error) {
return NextResponse.json(
{ error: 'Failed to fetch user', success: false },
{ status: 500 }
);
}
}
export async function PUT(
request: NextRequest,
{ params }: RouteParams
) {
try {
const body = await request.json();
const updatedUser = await updateUser(params.id, body);
return NextResponse.json({
data: updatedUser,
message: 'User updated successfully',
success: true,
});
} catch (error) {
return NextResponse.json(
{ error: 'Failed to update user', success: false },
{ status: 500 }
);
}
}
export async function DELETE(
request: NextRequest,
{ params }: RouteParams
) {
try {
await deleteUser(params.id);
return NextResponse.json({
message: 'User deleted successfully',
success: true,
});
} catch (error) {
return NextResponse.json(
{ error: 'Failed to delete user', success: false },
{ status: 500 }
);
}
}
الخطوة 7: إعداد Testing
تثبيت testing libraries:
npm install --save-dev @testing-library/react @testing-library/jest-dom @testing-library/user-event jest-environment-jsdom
إعداد Jest:
// jest.config.js
const nextJest = require('next/jest');
const createJestConfig = nextJest({
dir: './',
});
const customJestConfig = {
setupFilesAfterEnv: ['/jest.setup.js'],
moduleNameMapping: {
'^@/(.*)$': '/$1',
},
testEnvironment: 'jest-environment-jsdom',
};
module.exports = createJestConfig(customJestConfig);
ملف setup للاختبارات:
// jest.setup.js
import '@testing-library/jest-dom';
اختبار مكون Button:
// components/ui/Button.test.tsx
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { Button } from './Button';
describe('Button', () => {
it('renders with default props', () => {
render();
const button = screen.getByRole('button', { name: /click me/i });
expect(button).toBeInTheDocument();
});
it('calls onClick when clicked', async () => {
const user = userEvent.setup();
const handleClick = jest.fn();
render();
await user.click(screen.getByRole('button', { name: /click me/i }));
expect(handleClick).toHaveBeenCalledTimes(1);
});
it('is disabled when disabled prop is true', () => {
render();
const button = screen.getByRole('button', { name: /click me/i });
expect(button).toBeDisabled();
});
it('applies correct variant classes', () => {
render();
const button = screen.getByRole('button', { name: /delete/i });
expect(button).toHaveClass('bg-destructive');
});
});
الخطوة 8: تحسين الأداء
استخدام React.memo:
// components/UserCard.tsx
import { memo } from 'react';
import { User } from '@/types';
interface UserCardProps {
user: User;
onEdit?: (user: User) => void;
onDelete?: (userId: string) => void;
}
const UserCard = memo(({ user, onEdit, onDelete }) => {
return (
{user.name}
{user.email}
{onEdit && (
)}
{onDelete && (
)}
);
});
UserCard.displayName = 'UserCard';
export { UserCard };
استخدام useMemo و useCallback:
// components/UserList.tsx
import { useMemo, useCallback } from 'react';
import { User } from '@/types';
import { UserCard } from './UserCard';
interface UserListProps {
users: User[];
searchTerm: string;
onEditUser: (user: User) => void;
onDeleteUser: (userId: string) => void;
}
export function UserList({
users,
searchTerm,
onEditUser,
onDeleteUser,
}: UserListProps) {
const filteredUsers = useMemo(() => {
return users.filter((user) =>
user.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
user.email.toLowerCase().includes(searchTerm.toLowerCase())
);
}, [users, searchTerm]);
const handleEditUser = useCallback(
(user: User) => {
onEditUser(user);
},
[onEditUser]
);
const handleDeleteUser = useCallback(
(userId: string) => {
onDeleteUser(userId);
},
[onDeleteUser]
);
return (
{filteredUsers.map((user) => (
))}
);
}
الخطوة 9: إعداد CI/CD مع GitHub Actions
إنشاء workflow للاختبارات:
// .github/workflows/ci.yml
name: CI
on:
push:
branches: [main, develop]
pull_request:
branches: [main, develop]
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [16.x, 18.x]
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Setup Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run linting
run: npm run lint
- name: Run type checking
run: npm run type-check
- name: Run tests
run: npm run test:ci
- name: Build application
run: npm run build
deploy:
needs: test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: 18.x
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Build application
run: npm run build
- name: Deploy to Vercel
uses: amondnet/vercel-action@v25
with:
vercel-token: ${{ secrets.VERCEL_TOKEN }}
vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}
الخطوة 10: إعداد النشر
إعداد Vercel:
npm i -g vercel
vercel --prod
إعداد environment variables:
// .env.local
NEXT_PUBLIC_API_URL=https://api.example.com
DATABASE_URL=postgresql://username:password@localhost:5432/myapp
JWT_SECRET=your-secret-key-here
NEXTAUTH_SECRET=your-nextauth-secret
NEXTAUTH_URL=http://localhost:3000
إعداد build script:
// package.json
{
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint",
"lint:fix": "next lint --fix",
"type-check": "tsc --noEmit",
"test": "jest",
"test:watch": "jest --watch",
"test:ci": "jest --ci --coverage",
"storybook": "storybook dev -p 6006",
"build-storybook": "storybook build"
}
}
خاتمة
تهانينا! لقد نجحت في إعداد بيئة تطوير متكاملة لتطبيقات React.js المتقدمة مع TypeScript و Next.js. الآن لديك جميع الأدوات والمعارف المطلوبة لبناء تطبيقات ويب حديثة وقوية.
الميزات المضافة:
- TypeScript للتحقق من الأنواع
- Next.js للـ SSR والتطبيقات السريعة
- Zustand لإدارة الحالة
- Testing شامل مع Jest
- CI/CD مع GitHub Actions
- UI Components قابلة للإعادة
- API Routes محمية
- تحسينات الأداء
خطوات التطوير المستقبلية:
- إضافة Storybook للوثائق
- تكامل مع GraphQL
- إضافة PWA features
- تحسين SEO
- إضافة Real-time features
من خلال
Mohannad Hassounah
مشرف
معلومات الشرح
البرنامج:
React.js, TypeScript, Next.js
نظام التشغيل:
Linux
المستوى:
متقدم