مقدمه: اهمیت یادگیری پروژه‌محور در برنامه‌نویسی

یادگیری یک کتابخانه محبوب مانند React می‌تواند سفری هیجان‌انگیز باشد. شما ساعت‌ها ویدیوهای آموزشی می‌بینید، مستندات را می‌خوانید و با مفاهیمی مانند کامپوننت‌ها، JSX، State و Props آشنا می‌شوید. اما اغلب یک شکاف بزرگ بین دانش تئوری و توانایی عملی وجود دارد. شما مفاهیم را می‌شناسید، اما وقتی با یک صفحه خالی برای ساخت یک پروژه واقعی روبرو می‌شوید، نمی‌دانید از کجا شروع کنید. این مشکل، چالشی رایج در میان بسیاری از توسعه‌دهندگان در حال یادگیری است.

بهترین راه برای پر کردن این شکاف، «یادگیری پروژه‌محور» است. ساخت یک پروژه واقعی، هرچند کوچک، شما را مجبور می‌کند تا با چالش‌های عملی دست و پنجه نرم کنید، مفاهیم تئوری را به کار ببرید، با خطاها مواجه شوید و راه‌حل پیدا کنید. این فرآیند، دانش شما را تثبیت کرده و اعتماد به نفس لازم برای پذیرش پروژه‌های بزرگ‌تر را به شما می‌دهد.

در این مقاله، قصد داریم دقیقاً همین کار را انجام دهیم. ما به صورت گام به گام، یک اپلیکیشن لیست کارها (Todo App) ساده اما کامل را با React خواهیم ساخت. این راهنما شما را از مرحله آماده‌سازی محیط توسعه تا ساخت کامپوننت‌ها، مدیریت وضعیت برنامه و در نهایت، استقرار (Deploy) آن روی اینترنت همراهی خواهد کرد. اگر آماده‌اید تا دانش خود را به یک پروژه ملموس تبدیل کنید، با ما همراه باشید.


آماده‌سازی محیط توسعه

قبل از اینکه اولین خط کد React را بنویسیم، باید مطمئن شویم که تمام ابزارهای لازم را روی سیستم خود نصب کرده‌ایم. این مرحله مانند آماده کردن میز کار قبل از شروع یک پروژه نجاری است؛ باید همه چیز مرتب و در دسترس باشد.

۱. نصب Node.js و npm

شاید بپرسید چرا برای کار با یک کتابخانه فرانت‌اند مانند React به Node.js نیاز داریم؟ Node.js یک محیط اجرایی جاوااسکریپت در سمت سرور است، اما اکوسیستم توسعه فرانت‌اند مدرن به شدت به ابزارهای آن وابسته است. ما برای مدیریت پکیج‌ها، اجرای سرور توسعه محلی و بیلد کردن پروژه نهایی به Node.js و مدیر پکیج آن یعنی npm (Node Package Manager) نیاز داریم. npm به صورت خودکار همراه با Node.js نصب می‌شود.

برای نصب، به وب‌سایت رسمی Node.js مراجعه کرده و نسخه LTS (Long-Term Support) را که پایدارتر است، دانلود و نصب کنید. پس از نصب، می‌توانید با باز کردن ترمینال و وارد کردن دستورات node -v و npm -v از نصب صحیح آن‌ها مطمئن شوید.

۲. نصب یک ویرایشگر کد مناسب

شما برای نوشتن کد به یک ویرایشگر خوب نیاز دارید. امروزه Visual Studio Code (VS Code) به استاندارد صنعتی در میان توسعه‌دهندگان وب تبدیل شده است. این ویرایشگر رایگان، سریع و بسیار قدرتمند است و دارای یک ترمینال یکپارچه، قابلیت‌های دیباگ عالی و هزاران افزونه (Extensions) مفید است که می‌تواند بهره‌وری شما را به شدت افزایش دهد. افزونه‌هایی مانند Prettier برای فرمت‌بندی خودکار کد و ES7+ React/Redux/React-Native snippets برای تولید سریع قطعه کدهای آماده React، بسیار توصیه می‌شوند.

۳. آشنایی با ابزار راه‌اندازی پروژه: Vite

در گذشته، ابزار اصلی برای ساخت پروژه‌های ری‌اکت، Create React App بود. اما امروزه، Vite (که "ویت" تلفظ می‌شود) به دلیل سرعت فوق‌العاده بالا و تجربه توسعه بهتر، به انتخاب اول بسیاری از توسعه‌دهندگان تبدیل شده است. Vite یک ابزار ساخت (Build Tool) مدرن است که یک پروژه React را با تمام تنظیمات لازم برای شروع، در چند ثانیه برای شما آماده می‌کند. ما در این آموزش از Vite استفاده خواهیم کرد.

ایجاد اولین پروژه

حالا که محیط توسعه ما آماده است، زمان آن رسیده که اولین پروژه React خود را ایجاد کنیم. این کار با Vite بسیار ساده است.

۱. راه‌اندازی پروژه با Vite

ترمینال یا خط فرمان خود را باز کنید و دستور زیر را در مسیری که می‌خواهید پروژه خود را بسازید، وارد کنید:

$ npm create vite@latest my-todo-app -- --template react

این دستور از npm می‌خواهد تا آخرین نسخه از پکیج create-vite را اجرا کند. نام پروژه ما my-todo-app خواهد بود و با پرچم --template react مشخص می‌کنیم که می‌خواهیم یک پروژه مبتنی بر React بسازیم.

۲. ورود به پروژه و نصب وابستگی‌ها

پس از اجرای موفقیت‌آمیز دستور بالا، یک پوشه جدید به نام my-todo-app ایجاد می‌شود. با دستورات زیر وارد این پوشه شده و وابستگی‌های پروژه را نصب می‌کنیم:

$ cd my-todo-app
$ npm install

دستور npm install فایل package.json را می‌خواند و تمام پکیج‌های مورد نیاز پروژه (مانند خود React) را در پوشه‌ای به نام node_modules دانلود و نصب می‌کند.

۳. بررسی ساختار پوشه‌ها

Vite یک ساختار پوشه تمیز و منطقی برای ما ایجاد می‌کند. برای دیدن ساختار پوشه‌ها، می‌توانید از دستور tree استفاده کنید:

$ tree my-todo-app
my-todo-app
├── node_modules
├── package.json
├── package-lock.json
├── public
├── src
├── index.html
├── vite.config.js
                    

همانطور که می‌بینید، این ساختار شامل موارد زیر است:

  • node_modules: شامل تمام وابستگی‌های نصب شده پروژه است.
  • package.json: فایل پیکربندی پروژه که شامل اطلاعات پروژه و وابستگی‌ها است.
  • package-lock.json: فایل قفل وابستگی‌ها که نسخه‌های دقیق پکیج‌ها را مشخص می‌کند.
  • public: دایرکتوری که شامل فایل‌های استاتیک مانند تصاویر و آیکون‌ها است.
  • src: دایرکتوری اصلی کد منبع که شامل فایل‌های React ما خواهد بود.
  • index.html: فایل HTML اصلی که اپلیکیشن React در آن رندر می‌شود.
  • vite.config.js: فایل پیکربندی Vite که تنظیمات بیلد و توسعه را مشخص می‌کند.

۴. اجرای سرور توسعه

برای دیدن پروژه خود در مرورگر، کافی است دستور زیر را در ترمینال اجرا کنید:

$ npm run dev

این دستور یک سرور توسعه محلی را راه‌اندازی می‌کند و یک آدرس (معمولاً http://localhost:5173) را به شما می‌دهد. با باز کردن این آدرس در مرورگر، می‌توانید اپلیکیشن پیش‌فرض React را مشاهده کنید. یکی از بهترین ویژگی‌های Vite، قابلیت Hot Module Replacement (HMR) است که باعث می‌شود هر تغییری که در کد خود ذخیره می‌کنید، بلافاصله و بدون نیاز به رفرش کردن صفحه، در مرورگر اعمال شود.

ساخت کامپوننت‌های اولیه (Todo App)

زمان آن رسیده که پروژه پیش‌فرض را پاک کرده و اپلیکیشن لیست کارهای خودمان را بسازیم. ایده اصلی React، شکستن رابط کاربری به قطعات کوچک و قابل استفاده مجدد به نام «کامپوننت» است. برای برنامه ما، به چند کامپوننت نیاز خواهیم داشت.

۱. پاک‌سازی پروژه و ساختار کامپوننت‌ها

ابتدا محتوای فایل‌های App.jsx و App.css را کاملاً پاک کنید. ما اپلیکیشن خود را به چند کامپوننت تقسیم می‌کنیم:

  • App: کامپوننت اصلی که تمام کامپوننت‌های دیگر را در خود جای می‌دهد و وضعیت کلی برنامه را مدیریت می‌کند.
  • TodoForm: یک فرم با یک ورودی متنی و یک دکمه برای اضافه کردن کارهای جدید.
  • TodoList: لیستی که تمام کارهای اضافه شده را نمایش می‌دهد.
  • TodoItem: کامپوننتی برای نمایش یک کار تکی، همراه با دکمه‌های حذف و تکمیل.

۲. ساخت کامپوننت فرم (TodoForm)

در پوشه src یک پوشه جدید به نام components بسازید. داخل آن، فایلی به نام TodoForm.jsx ایجاد کنید. این کامپوننت یک فرم ساده خواهد بود. کد اولیه آن می‌تواند به این شکل باشد:

Copy Icon src/components/ToDoForm.jsx
function TodoForm() {
  return (
    <form>
      <input type="text" placeholder="Add a new task..." />
      <button type="submit">Add</button>
    </form>
  );
}

export default TodoForm;

JSX یک سینتکس شبیه به HTML است که درون جاوااسکریپت نوشته می‌شود. این سینتکس به شما اجازه می‌دهد تا ساختار کامپوننت‌ها را به صورت خوانا و قابل فهم بنویسید. البته فعلا این کامپوننت فقط یک فرم ساده است و ما بعداً منطق آن را با استفاده از JSX اضافه خواهیم کرد.

۳. ساخت کامپوننت آیتم کار (TodoItem) و لیست کارها (TodoList)

به همین ترتیب، فایل‌های TodoItem.jsx و TodoList.jsx را ایجاد کنید. TodoItem یک کار را نمایش می‌دهد و TodoList لیستی از این آیتم‌ها را رندر می‌کند. فعلاً آن‌ها را با محتوای ساده پر می‌کنیم و بعداً منطق آن‌ها را کامل خواهیم کرد.

Copy Icon src/components/ToDoItem.jsx
function TodoItem({ task }) {
  return (
    <div className="todo-item">
      <span>{task}</span>
      <button>Remove</button>
    </div>
  );
}

export default TodoItem;

و برای TodoList:

Copy Icon src/components/ToDoList.jsx
import TodoItem from './TodoItem';

function TodoList({ tasks }) {
  return (
    <div className="todo-list">
      {tasks.map((task, index) => (
        <TodoItem key={index} task={task} />
      ))}
    </div>
  );
}

export default TodoList;

در اینجا، ما از متد map برای تکرار روی آرایه tasks استفاده می‌کنیم و برای هر کار یک کامپوننت TodoItem ایجاد می‌کنیم. پراپ task را به عنوان ورودی به کامپوننت TodoItem ارسال می‌کنیم.

مدیریت State و Props

تا اینجا ما فقط ساختار ظاهری (UI) را طراحی کردیم. برای اینکه برنامه ما پویا شود و کاربر بتواند با آن تعامل کند، باید از دو مفهوم بنیادی در React استفاده کنیم: State و Props.

Props چیست؟ (ارسال داده از والد به فرزند)

Props (مخفف Properties) مکانیزمی برای ارسال داده از یک کامپوننت والد به کامپوننت فرزند است. پراپ‌ها «فقط خواندنی» (Read-only) هستند، یعنی کامپوننت فرزند نمی‌تواند آن‌ها را مستقیماً تغییر دهد. این مانند دادن یک دستورالعمل به یک کارمند است؛ کارمند دستور را اجرا می‌کند اما نمی‌تواند خود دستور را عوض کند.

State چیست؟ (حافظه داخلی کامپوننت)

State یا وضعیت، حافظه داخلی یک کامپوننت است. هر داده‌ای که با تعامل کاربر تغییر می‌کند (مانند لیست کارها یا متن داخل یک فیلد ورودی) باید در State نگهداری شود. ویژگی کلیدی State این است که هرگاه مقدار آن تغییر کند، React به صورت خودکار کامپوننت را مجدداً رندر (Re-render) می‌کند تا رابط کاربری با داده‌های جدید به‌روز شود. برای مدیریت State در کامپوننت‌های تابعی، از هوک useState استفاده می‌کنیم.

پیاده‌سازی منطق برنامه

منطق اصلی برنامه ما در کامپوننت App قرار خواهد گرفت. ما لیست کارها را در State این کامپوننت نگهداری می‌کنیم:

Copy Icon App.jsx
import React, { useState } from 'react';
import TodoForm from './components/TodoForm';
import TodoList from './components/TodoList';
function App() {    
    const [todos, setTodos] = useState([]);
    return (
        <div>
            <TodoForm />
            <TodoList todos={todos} />
        </div>
    );
}
export default App;

در اینجا، ما از هوک useState برای ایجاد یک State به نام todos استفاده می‌کنیم که یک آرایه خالی است. این آرایه لیست کارهای ما را نگه می‌دارد. تابع setTodos برای به‌روزرسانی این State استفاده می‌شود.

حالا باید منطق اضافه کردن کار جدید را در TodoForm پیاده‌سازی کنیم. ما یک تابع به نام addTodo در App تعریف می‌کنیم که یک کار جدید به لیست کارها اضافه می‌کند:

Copy Icon App.jsx
import React, { useState } from 'react';
import TodoForm from './components/TodoForm';
import TodoList from './components/TodoList';
function App() {    
    const [todos, setTodos] = useState([]);
    const addTodo = (todo) => {
        setTodos([...todos, todo]);
    };
    return (
        <div>
            <TodoForm addTodo={addTodo} />
            <TodoList todos={todos} />
        </div>
    );
}
export default App;

در اینجا، تابع addTodo یک پارامتر به نام todo می‌گیرد و آن را به آرایه todos اضافه می‌کند. ما این تابع را به عنوان یک پراپ به کامپوننت TodoForm ارسال می‌کنیم تا بتوانیم از آن برای اضافه کردن کارهای جدید استفاده کنیم.

حالا در TodoForm، باید یک فرم بسازیم که کاربر بتواند متن کار جدید را وارد کند و با کلیک روی دکمه «اضافه کردن»، آن کار به لیست اضافه شود. ما از هوک useState برای نگهداری مقدار ورودی استفاده می‌کنیم:

Copy Icon ToDoForm.jsx
import React, { useState } from 'react';
function TodoForm({ addTodo }) {
    const [inputValue, setInputValue] = useState('');
    const handleSubmit = (e) => {
        e.preventDefault();
        addTodo(inputValue);
        setInputValue('');
    };
    return (
        <form onSubmit={handleSubmit}>
            <input
                type="text"
                value={inputValue}
                onChange={(e) => setInputValue(e.target.value)}
                placeholder="Add new task"
            />
            <button type="submit">Add</button>
        </form>
    );
}

در اینجا، ما یک State به نام inputValue داریم که مقدار ورودی کار جدید را نگه می‌دارد. با هر بار تغییر در فیلد ورودی، تابع setInputValue فراخوانی می‌شود تا مقدار جدید را ذخیره کند. وقتی کاربر فرم را ارسال می‌کند، تابع handleSubmit اجرا می‌شود که کار جدید را به لیست اضافه کرده و فیلد ورودی را خالی می‌کند.

در نهایت، کامپوننت TodoList باید لیست کارها را نمایش دهد. ما از پراپ todos که از کامپوننت App دریافت کردیم، برای رندر کردن هر کار استفاده می‌کنیم:

Copy Icon ToDoList.jsx
import React from 'react';
import TodoItem from './TodoItem';
function TodoList({ todos }) {
    return (
        <ul>
            {todos.map((todo, index) => (
                <TodoItem key={index} todo={todo} />
            ))}
        </ul>
    );
}

در اینجا، ما از متد map برای تکرار روی آرایه todos استفاده می‌کنیم و برای هر کار یک کامپوننت TodoItem ایجاد می‌کنیم. پراپ todo را به عنوان ورودی به کامپوننت TodoItem ارسال می‌کنیم.

استایل‌دهی و بهبود تجربه کاربری

حالا که منطق برنامه را پیاده‌سازی کردیم، وقت آن است که کمی به ظاهر اپلیکیشن برسیم. استایل‌دهی مناسب می‌تواند تجربه کاربری را به شدت بهبود بخشد.

۱. اضافه کردن استایل‌های پایه

در فایل App.css، می‌توانیم استایل‌های پایه برای کامپوننت‌ها اضافه کنیم. مثلاً:

Copy Icon App.css
.app-container {
   max-width: 600px;
    margin: 0 auto; 
    padding: 20px;
    font-family: Arial, sans-serif;
    background-color: #f9f9f9;
    border-radius: 8px;
    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}

.todo-list {
    list-style: none;
    padding: 0;
}

.todo-item {
    padding: 12px 16px;
    border-bottom: 1px solid #eee;
}

.todo-item:last-child {
    border-bottom: none;
}

.todo-item span {
    margin-right: 10px;
}

.todo-item button {
    background-color: #ff4d4d;
    color: white;
    border: none;
    border-radius: 4px;
    padding: 6px 12px;
    cursor: pointer;
}

.todo-item button:hover {
    background-color: #ff1a1a;
}

.todo-form {
    display: flex;
    justify-content: space-between;
    margin-top: 20px;
}

.todo-form input {
    flex: 1;
    padding: 10px;
    border: 1px solid #ccc;
    border-radius: 4px;
    margin-right: 10px;
}

.todo-form button {
    background-color: #4CAF50;
    color: white;
    border: none;
    border-radius: 4px;
    padding: 10px 15px;
    cursor: pointer;
}

.todo-form button:hover {
    background-color: #45a049;
}

این استایل‌ها به اپلیکیشن ظاهری تمیز و کاربرپسند می‌دهند. می‌توانید با توجه به سلیقه خود، رنگ‌ها و فونت‌ها را تغییر دهید.

۲. بهبود تجربه کاربری با انیمیشن‌ها

برای بهبود تجربه کاربری، می‌توانیم از انیمیشن‌های CSS برای اضافه کردن جلوه‌های حرکتی استفاده کنیم. مثلاً وقتی یک کار جدید اضافه می‌شود یا حذف می‌شود، می‌توانیم از انیمیشن Fade-in/Fade-out استفاده کنیم. این کار باعث می‌شود اپلیکیشن ما حرفه‌ای‌تر به نظر برسد.

Copy Icon App.css
.todo-item {
    transition: all 0.3s ease;
}

.todo-item.fade-enter {
    opacity: 0;
    transform: translateY(-10px);
}

.todo-item.fade-enter-active {
    opacity: 1;
    transform: translateY(0);
}

.todo-item.fade-exit {
    opacity: 1;
    transform: translateY(0);
}

.todo-item.fade-exit-active {
    opacity: 0;
    transform: translateY(-10px);
}

برای استفاده از این انیمیشن‌ها، باید کلاس‌های مناسب را در هنگام اضافه کردن یا حذف کارها به کامپوننت TodoItem اضافه کنیم. این کار معمولاً با استفاده از کتابخانه‌هایی مانند React Transition Group انجام می‌شود که مدیریت انیمیشن‌ها را ساده‌تر می‌کند.

با این تغییرات، اپلیکیشن ما نه تنها کار می‌کند بلکه از نظر ظاهری نیز جذاب‌تر و کاربرپسندتر شده است. در مرحله بعد، به سراغ استقرار (Deploy) اپلیکیشن خود خواهیم رفت تا بتوانیم آن را با دیگران به اشتراک بگذاریم.

استقرار (Deploy) اپلیکیشن

حالا که اپلیکیشن Todo App ما آماده است، وقت آن رسیده که آن را در اینترنت منتشر کنیم تا دیگران هم بتوانند از آن استفاده کنند. استقرار یک اپلیکیشن React معمولاً شامل بیلد کردن پروژه و آپلود فایل‌های بیلد شده روی یک سرویس میزبانی وب است.

۱. بیلد کردن پروژه

برای آماده‌سازی اپلیکیشن برای استقرار، باید آن را بیلد کنیم. این کار با اجرای دستور زیر در ترمینال انجام می‌شود:

$ npm run build

این دستور یک پوشه جدید به نام dist ایجاد می‌کند که حاوی تمام فایل‌های بیلد شده اپلیکیشن شماست. این پوشه شامل HTML، CSS و JavaScript فشرده‌شده است که برای بارگذاری در مرورگر آماده شده‌اند.

۲. انتخاب سرویس میزبانی

برای استقرار اپلیکیشن React، می‌توانید از سرویس‌های مختلفی استفاده کنید. برخی از محبوب‌ترین گزینه‌ها عبارتند از:

  • Vercel: یکی از بهترین گزینه‌ها برای استقرار اپلیکیشن‌های React است. بسیار سریع و آسان راه‌اندازی می‌شود.
  • Netlify: مشابه Vercel، با امکانات عالی برای استقرار و مدیریت پروژه‌های فرانت‌اند.
  • GitHub Pages: اگر پروژه شما در GitHub قرار دارد، می‌توانید به راحتی آن را روی GitHub Pages منتشر کنید.
  • AWS Amplify: یک سرویس قدرتمند از آمازون برای استقرار اپلیکیشن‌های وب و موبایل.

۳. آپلود فایل‌ها

پس از انتخاب سرویس میزبانی، کافی است پوشه dist را آپلود کنید. هر سرویس راهنمای خاص خود را برای آپلود فایل‌ها دارد. به عنوان مثال، در Vercel کافی است پوشه dist را به داشبورد پروژه خود بکشید و رها کنید. Vercel به صورت خودکار فایل‌ها را پردازش کرده و اپلیکیشن شما را در دسترس عموم قرار می‌دهد.

پس از آپلود، سرویس میزبانی یک URL به شما می‌دهد که می‌توانید با استفاده از آن اپلیکیشن خود را مشاهده کنید. این URL به صورت عمومی در دسترس است و هر کسی می‌تواند با باز کردن آن در مرورگر، اپلیکیشن شما را ببیند.