آموزش حرفهای React JSX: از اصول تا الگوهای پیشرفته
JSX یک سینتکس شبیه HTML است که داخل جاوااسکریپت استفاده میشود و بهصورت Declarative رابط کاربری را توصیف میکند. این آموزش از مبانی تا تکنیکهای حرفهای (شرطیسازی، لیستها و کلیدها، مدیریت state، هوکها، پرفورمنس و الگوهای پیشرفته) را پوشش میدهد.
۱) JSX چیست و چرا؟
- خوانایی بالا و نزدیکی به ساختار UI
- اسکوپ و منطق جاوااسکریپت در کنار View
- بهینهسازی هوشمند توسط React و ابزارهای ساخت
قواعد JSX
- کامپوننتها باید یک ریشه داشته باشند (از <div> یا <React.Fragment> استفاده کنید).
- بهجای
class
ازclassName
استفاده کنید. - استایل inline بهصورت آبجکت جاوااسکریپت است (camelCase).
- عبارتهای JS را داخل
{}
قرار دهید.
// یک کامپوننت ساده function Hello() { const name = "Piero"; return ( &lt;div className="box"&gt; &lt;h2&gt;سلام، {name}!&lt;/h2&gt; &lt;/div&gt; ); }
۲) درج متغیرها و عبارات در JSX
هر عبارت جاوااسکریپت را میتوانید با آکولاد در JSX بنویسید.
const price = 129_000; const user = { firstName: "Sara", lastName: "Karimi" }; export default function Card() { return ( &lt;section&gt; مبلغ: {price.toLocaleString("fa-IR")} نام: {${user.firstName} ${user.lastName}} {price &gt; 100000 ? &lt;strong&gt;پیشنهاد ویژه!&lt;/strong&gt; : null} &lt;/section&gt; ); }
۳) شرطیسازی در JSX
از عملگر سهتایی، AND منطقی یا جداسازی منطق قبل از return استفاده کنید.
// 1) سهتایی {isLoading ? &lt;Spinner /&gt; : &lt;DataList /&gt;} // 2) AND منطقی (برای نمایش شرطی کوتاه) {error &amp;&amp; خطا: {error.message} } // 3) جداسازی منطق let content; if (isLoading) content = &lt;Spinner /&gt;; else if (error) content = &lt;ErrorView /&gt;; else content = &lt;DataList /&gt;; return &lt;div&gt;{content}&lt;/div&gt;;
۴) رندر لیستها و کلیدها (keys)
برای عملکرد و جلوگیری از باگ در Diff، به هر آیتم یک
key
یکتا بدهید (از id پایدار استفاده کنید).const items = [ { id: "a1", title: "React" }, { id: "b2", title: "JSX" }, { id: "c3", title: "Hooks" }, ]; function List() { return ( &lt;ul&gt; {items.map(item =&gt; ( &lt;li key={item.id}&gt;{item.title}&lt;/li&gt; ))} &lt;/ul&gt; ); }
۵) پراپسها، State و رویدادها
پراپسها
function Button({ children, onClick, type = "button" }) { return &lt;button type={type} onClick={onClick}&gt;{children}&lt;/button&gt;; } // استفاده &lt;Button onClick={() =&gt; alert("clicked!")}&gt;کلیک&lt;/Button&gt;State و رویدادها با Hooks
import { useState } from "react"; function Counter() { const [count, setCount] = useState(0); return ( &lt;div&gt; شمارنده: {count} &lt;button onClick={() =&gt; setCount((c) =&gt; c + 1)}&gt;افزایش&lt;/button&gt; &lt;/div&gt; ); }کنترل فرم (Controlled Components)
import { useState } from "react"; function LoginForm() { const [email, setEmail] = useState(""); const submit = (e) =&gt; { e.preventDefault(); console.log({ email }); }; return ( &lt;form onSubmit={submit}&gt; &lt;input value={email} onChange={(e) =&gt; setEmail(e.target.value)} placeholder="ایمیل" /&gt; &lt;button&gt;ارسال&lt;/button&gt; &lt;/form&gt; ); }
۶) Fragment، Portal و Context
Fragment
return ( &lt;&gt; &lt;Header /&gt; &lt;Main /&gt; &lt;/&gt; );Portal (رندر خارج از سلسلهمراتب معمول)
import { createPortal } from "react-dom"; function Modal({ children }) { return createPortal( &lt;div className="backdrop"&gt;{children}&lt;/div&gt;, document.getElementById("modal-root") ); }Context (مدیریت وضعیت سراسری سبک)
import { createContext, useContext } from "react"; const ThemeContext = createContext("light"); function App() { return ( &lt;ThemeContext.Provider value="dark"&gt; &lt;Toolbar /&gt; &lt;/ThemeContext.Provider&gt; ); } function Toolbar() { const theme = useContext(ThemeContext); return &lt;div data-theme={theme}&gt;Toolbar&lt;/div&gt;; }
۷) الگوهای حرفهای با Hooks
بهینهسازی پرفورمنس: memo, useMemo, useCallback
import React, { useMemo, useCallback } from "react"; const Expensive = React.memo(function Expensive({ value }) { console.log("render"); return &lt;div&gt;محاسبه: {value}&lt;/div&gt;; }); function Dashboard({ items }) { const total = useMemo(() =&gt; items.reduce((s, x) =&gt; s + x, 0), [items]); const handleClick = useCallback(() =&gt; console.log("clicked"), []); return ( &lt;div&gt; جمع: {total} &lt;button onClick={handleClick}&gt;لاگ&lt;/button&gt; &lt;Expensive value={total} /&gt; &lt;/div&gt; ); }کداسپلیتینگ با lazy و Suspense
import React from "react"; const Chart = React.lazy(() =&gt; import("./Chart")); function Reports() { return ( &lt;React.Suspense fallback={&lt;div&gt;در حال بارگذاری...&lt;/div&gt;}&gt; &lt;Chart /&gt; &lt;/React.Suspense&gt; ); }Error Boundary
import React from "react"; class ErrorBoundary extends React.Component { state = { hasError: false }; static getDerivedStateFromError() { return { hasError: true }; } componentDidCatch(err, info) { console.error(err, info); } render() { return this.state.hasError ? &lt;h3&gt;خطایی رخ داد&lt;/h3&gt; : this.props.children; } } export default ErrorBoundary;
۸) الگوی «بالابردن State» و ترکیبپذیری
import { useState } from "react"; function Parent() { const [value, setValue] = useState(""); return ( &lt;&gt; &lt;Input value={value} onChange={setValue} /&gt; &lt;Preview value={value} /&gt; &lt;/&gt; ); } function Input({ value, onChange }) { return &lt;input value={value} onChange={(e) =&gt; onChange(e.target.value)} /&gt;; } function Preview({ value }) { return پیشنمایش: {value} ; }
۹) استایلدهی در JSX
گزینهها: CSS Module، Styled Components، Tailwind، یا استایل inline.
// استایل inline &lt;div style={{ padding: 12, borderRadius: 8 }} /&gt; // CSS Module import styles from "./box.module.css"; &lt;div className={styles.box}&gt;Box&lt;/div&gt;
۱۰) دسترسیپذیری (a11y) و SEO فرانتاند
- استفاده از تگهای معنایی (<main>, <nav>, <header>)
- مشخصکردن alt برای تصاویر
- مدیریت فوکوس و نسبت کنتراست مناسب
۱۱) تست کامپوننتها
// React Testing Library import { render, screen } from "@testing-library/react"; import Button from "./Button"; test("renders label", () =&gt; { render(&lt;Button&gt;سلام&lt;/Button&gt;); expect(screen.getByText("سلام")).toBeInTheDocument(); });
۱۲) نمونهٔ اپ کوچک: فهرست کارها (Todo)
import { useState } from "react"; export default function TodoApp() { const [items, setItems] = useState([]); const [text, setText] = useState(""); const add = () =&gt; { if (!text.trim()) return; setItems((prev) =&gt; [...prev, { id: crypto.randomUUID(), text }]); setText(""); }; const remove = (id) =&gt; setItems((prev) =&gt; prev.filter(i =&gt; i.id !== id)); return ( &lt;div className="todo"&gt; &lt;h2&gt;کارها&lt;/h2&gt; &lt;input value={text} onChange={(e) =&gt; setText(e.target.value)} /&gt; &lt;button onClick={add}&gt;افزودن&lt;/button&gt; &lt;ul&gt; {items.map(i =&gt; ( &lt;li key={i.id}&gt; {i.text} &lt;button onClick={() =&gt; remove(i.id)}&gt;حذف&lt;/button&gt; &lt;/li&gt; ))} &lt;/ul&gt; &lt;/div&gt; ); }
۱۳) نکات حرفهای برای پروژههای واقعی
- ساختار پوشهها بر اساس دامنه (Feature-Sliced) بهجای تفکیک فایلمحور.
- TypeScript برای خودمستندسازی و ایمنی تایپی.
- ESLint + Prettier برای یکپارچگی کد.
- استفاده از React Query / SWR برای مدیریت سرور استیت.
- Next.js برای SSR/SSG، بهبود SEO و رندر سمت سرور.
پرسشهای متداول (FAQ)
تفاوت JSX و HTML؟ JSX یک سینتکس داخل جاوااسکریپت است؛ برخی ویژگیها متفاوتاند (مثل className، onClick).
آیا بدون Babel میتوان JSX نوشت؟ نه، JSX باید به جاوااسکریپت کامپایل شود (Vite/Next.js/CRA قدیمی).
راه بهینهسازی سرعت رندر چیست؟ کلیدهای پایدار، memo/useMemo/useCallback، تقسیم کد با lazy/Suspense و جلوگیری از رندرهای غیرضروری.