هوک ها در React
هوک Hook ها توابعى هستند كه به ما اجازه میدهند از قابليتهاى رى اكت، مانند state در كامپوننتهاى تابعى استفاده كنيم. به عبارتى، Hookها ابزارى هستند كه به ما كمك مى كنند تا در كامپوننتهاى تابعى كارهايى را انجام دهيم كه قبلاً فقط در كامپوننت هاى كلاسى ممكن بود.
هوک های مهم React
- useState
- useEffect
- useContext
تاثیرات مستقیم (Pure Effects)
تأثيرات مستقيم فقط به اU برنامه مربوط هستند و تغييرات مستقيم در خود كامپوننت ايجاد مى كنند. مثلا وقتى كه شما داخل كامپوننت يك State تعريف مى كنيد وآن را تغيير مى دهيد بطور مستقيم روى كاميوننت تاثير ايجاد كرده ايد.
- فقط مربوط به ui هستند ونتيجه رندرينگ درون كامپوننت است.
- هيچ تغييرى در سيستم يا مرورگر خارج از كامپوننت ايجاد نمى كنند.
- در مثال ما، بروزرسانى مقدار count درون صفحه ونمايش آن دريک تگ يک تأثير مستقیم است.
تاثیرات غیر مستقیم (Side Effects)
يک side effect زمانى رخ مى دهد كه يک عمل خارج از رندرينگ معمول كاميوننت انجام شود، مثلا وقتى كه به سرور درخواست میفرستيم، يا دادهاى را در local storage ذخيره مى كنيم، يا عنوان صفحه را تغيير مى دهيم. اين نوع اعمال خارج از كنترل مستقيم رندرينگ است ومعمولا به حالت به حالت کامپوننت وابسته نیستند.
- خارج از رندرینگ ui رخ میدهد.
- ممكن است روى سيستم يا مرورگر تأثير بگذارد (مثل تغيير عنوان صفحه، ارسال درخواست API. يا ذخيره سازى داده ها).
مثال: اجرای غیرضروری (Side Effects)
import { useState } from "react"
export default function Sample() {
const [count, setCount] = useState(0)
const [count2, setCount2] = useState(0)
document.title = count // اثر جانبی: بهروزرسانی عنوان صفحه مرورگر
return (
<div>
<h1>Count 1: {count}</h1>
<button onClick={() => setCount(count + 1)}>افزایش Count 1</button>
<hr />
<h1>Count 2: {count2}</h1>
<button onClick={() => setCount2(count2 + 1)}>افزایش Count 2</button>
</div>
)
}در مثال بالا، خط document.title = count یک اثر جانبی است که عنوان صفحه مرورگر را با مقدار count بهروز میکند. به نظر میرسد که این بهروزرسانی فقط باید زمانی اتفاق بیفتد که count تغییر کرده است.
اما مشکل اینجاست که این خط مستقیماً در بدنه اصلی کامپوننت (Sample) قرار دارد. این بدان معناست که هر بار که کامپوننت Sample رندر میشود، این خط اجرا خواهد شد.
کامپوننت Sample در دو حالت رندر میشود:
- هنگامی که
countتغییر میکند (با کلیک روی “افزایش Count 1”). - هنگامی که
count2تغییر میکند (با کلیک روی “افزایش Count 2”).
این یعنی، حتی اگر count تغییر نکرده باشد، با تغییر count2 و رندر مجدد کامپوننت، کد document.title = count دوباره اجرا میشود. در این مثال ساده، اجرای مجدد این خط شاید بیضرر به نظر برسد، اما مشکل اساسی این است که منطق بهروزرسانی عنوان صفحه به منطق کلی رندر کامپوننت گره خورده است.
پیامدهای در دنیای واقعی:
این مشکل در سناریوهای پیچیدهتر میتواند عواقب جدی داشته باشد:
- فراخوانیهای API تکراری: تصور کنید به جای
document.title = count، کدی برای دریافت داده از یک API داشته باشیم. اگر این کد در بدنه اصلی کامپوننت باشد، با هر بار تعامل کاربر (حتی تعاملی که ربطی به دادههای API ندارد)، یک درخواست جدید به API ارسال میشود. این نه تنها منابع سرور را بیجهت مصرف میکند، بلکه تجربه کاربری را نیز به شدت مختل کرده و باعث کندی و عدم پاسخگویی برنامه میشود. - مصرف منابع: اجرای مکرر عملیات سنگین (مانند پردازش تصویر، محاسبات پیچیده) با هر رندر، منابع CPU و حافظه را به شدت مصرف میکند.
هوک useEffect در React
هوک useEffect به شما اجازه میدهد تا side effects (اثرات جانبی) را در کامپوننتهای تابعی انجام دهید.
اثرات جانبی کارهایی هستند که خارج از جریان اصلی رندر کامپوننت انجام میشوند، مانند:
- دریافت داده از API
- دستکاری DOM (Document Object Model)
- تنظیم تایمر
- ثبت رویدادها
1. ساختار کلی useEffect
useEffect(() => {
// کد شما برای اجرای اثر جانبی
// ...
return () => {
// کد پاکسازی (اختیاری)
// ...
}
}, [dependencies]) // آرایه وابستگیها (اختیاری)- تابع اول: کدی که میخواهید اجرا شود.
- تابع بازگشتی (اختیاری): برای پاکسازی منابع (مثلاً clearInterval یا unsubscribe) قبل از اجرای مجدد
useEffectیا هنگام unmount شدن کامپوننت. - آرایه وابستگیها (dependency array): مشخص میکند که
useEffectچه زمانی دوباره اجرا شود.
2. اجرای useEffect فقط یک بار
اگر آرایه وابستگیها خالی باشد []، useEffect فقط یک بار بعد از اولین رندر اجرا میشود.
3. اجرای useEffect با هر رندر
اگر آرایه وابستگیها را حذف کنید، useEffect بعد از هر بار رندر اجرا میشود. این حالت معمولاً توصیه نمیشود مگر اینکه دقیقاً همین رفتار را بخواهید.
import { useEffect } from "react"
export default function Sample() {
useEffect(() => {
console.log("Component rendered or re-rendered")
}) // بدون آرایه وابستگی
return <div>محتوا</div>
}4. اجرای useEffect با تغییر وابستگیها
اگر آرایه وابستگیها مقادیری داشته باشد، useEffect فقط زمانی اجرا میشود که یکی از مقادیر وابستگی تغییر کند.
import { useState, useEffect } from "react"
export default function Sample() {
const [count, setCount] = useState(0)
const [name, setName] = useState("کاربر")
useEffect(() => {
console.log(`Count changed to: ${count}`)
// میتوانید اینجا کارهای مرتبط با count را انجام دهید
}, [count]) // فقط وقتی count تغییر کند اجرا میشود
useEffect(() => {
console.log(`Name changed to: ${name}`)
// میتوانید اینجا کارهای مرتبط با name را انجام دهید
}, [name]) // فقط وقتی name تغییر کند اجرا میشود
return (
<div>
<button onClick={() => setCount(count + 1)}>افزایش count</button>
<button onClick={() => setName("ادمین")}>تغییر نام</button>
<p>Count: {count}</p>
<p>Name: {name}</p>
</div>
)
}جمعبندی
- از
useEffectبرای مدیریت اثرات جانبی در کامپوننتهای تابعی استفاده میشود. - با استفاده از آرایه وابستگیها
[]، میتوانیم کنترل کنیم کهuseEffectچه زمانی اجرا شود (یک بار، با هر رندر، یا با تغییر یک مقدار خاص). - تابع بازگشتی در
useEffectبرای پاکسازی منابع استفاده میشود. - این هوک برای کارهایی مانند fetching داده، اشتراک در رویدادها و دستکاری DOM بسیار مفید است.
فراخوانی API با useEffect
import { useState, useEffect } from "react"
export default function Sample() {
let [users, setUsers] = useState([])
useEffect(() => {
console.log("Fetching users...")
fetch("https://jsonplaceholder.typicode.com/users")
.then(response => response.json())
.then(data => setUsers(data))
.catch(error => console.error(error))
}
, [])
if (users.length==0) {
return <h1>در حال بارگذاری کاربران...</h1>
}
return (
<div>
<h1>لیست کاربران</h1>
<ul>
{users.map((user) => (
<li key={user.id}>{user.name} ({user.email})</li>
))}
</ul>
</div>
)
}چرخه حیات کامپوننت (Component Lifecycle)
چرخه حیات کامپوننت به مراحلی گفته میشود که یک کامپوننت در طول عمر خود طی میکند؛ از زمانی که ساخته میشود تا زمانی که از DOM حذف شود.
در کامپوننتهای تابعی (Functional Components)، ما چرخه حیات را با استفاده از هوک useEffect مدیریت میکنیم.
مراحل اصلی چرخه حیات
1. Mount (ساخته شدن)
زمانی که کامپوننت برای اولین بار در DOM رندر میشود.
معادل در useEffect:
useEffect(() => {
console.log("Component Mounted")
}, [])آرایه وابستگی خالی [] باعث میشود این کد فقط یک بار بعد از اولین رندر اجرا شود.
2. Update (بهروزرسانی)
وقتی state یا props تغییر کند، کامپوننت دوباره رندر میشود.
useEffect(() => {
console.log("Component Updated")
}, [someState])هر زمان someState تغییر کند، این effect اجرا میشود.
3. Unmount (حذف شدن)
زمانی که کامپوننت از DOM حذف میشود.
برای مدیریت این مرحله از تابع پاکسازی (cleanup function) استفاده میکنیم.
تابع پاکسازی (Cleanup Function)
تابع بازگشتی که داخل useEffect تعریف میکنیم، برای جلوگیری از memory leak (نشت حافظه) استفاده میشود.
این تابع در دو حالت اجرا میشود:
- قبل از اجرای مجدد
useEffect - هنگام unmount شدن کامپوننت
مثال: اضافه و حذف Event Listener
import { useEffect } from "react"
export default function Sample() {
useEffect(() => {
let clickHandler = () => {
console.log("روی پنجره کلیک شد")
}
// اضافه کردن event listener هنگام mount
window.addEventListener("click", clickHandler)
// تابع پاکسازی
return () => {
// حذف event listener هنگام unmount
window.removeEventListener("click", clickHandler)
}
}, []) // فقط یک بار اجرا شود
}چرا Cleanup مهم است؟
اگر event listener را حذف نکنیم:
- با هر بار mount شدن کامپوننت، یک listener جدید اضافه میشود.
- باعث اجرای چندباره کد میشود.
- ممکن است منجر به memory leak شود.
- عملکرد برنامه کاهش پیدا میکند.
مثال دیگر: پاکسازی Interval
import { useEffect, useState } from "react"
export default function Timer() {
const [count, setCount] = useState(0)
useEffect(() => {
const interval = setInterval(() => {
setCount((prev) => prev + 1)
}, 1000)
return () => {
clearInterval(interval)
}
}, [])
return <h1>{count}</h1>
}جمعبندی
چرخه حیات کامپوننت شامل سه مرحله اصلی است:
- ✅ Mount → ساخته شدن کامپوننت
- 🔄 Update → تغییر state یا props
- ❌ Unmount → حذف شدن کامپوننت
در کامپوننتهای تابعی، با استفاده از useEffect میتوان این مراحل را مدیریت کرد.
تابع پاکسازی (Cleanup Function):
- برای جلوگیری از memory leak استفاده میشود.
- قبل از اجرای مجدد effect یا هنگام unmount اجرا میشود.
- برای حذف event listener، متوقف کردن interval و آزادسازی منابع ضروری است.
استایلدهی به روش CSS Modules
در این روش کلاسها به صورت محلی (Scoped) تعریف میشوند و فقط در همان فایل قابل دسترس هست و تداخل ایجاد نمیکنند.
import styles from "./MyComponent.module.css" // نام گزاری فایل
function MyComponent() {
{/* کلاس دهی با .styles */}
return <div className={styles.container}>محتوا</div>
}- نام گذاری: اخر نام فایل css ها باید از
.module.cssاستفاده کرد (مثلاًMyComponent.module.css) - کلاس دهی: نام کلاس ها را در این روش بجای رشته (string) باید بصورت آبجکت جاوااسکریپت و با
.stylesنوشت. (مثلclassName={styles.container}) .
مزیت:
- جلوگیری از تداخل نام کلاسها
- مناسب برای پروژههای بزرگ
استایلدهی به روش Styled Components
روش Styled Components یک روش CSS-in-JS است؛ یعنی CSS را مستقیماً داخل فایل جاوااسکریپت مینویسیم و یک کامپوننت استایلدهیشده میسازیم.
این روش با کتابخانههایی مثل:
styled-components@emotion/styled
انجام میشود.
نصب (برای styled-components)
npm install styled-componentsساخت یک کامپوننت استایلدهیشده
import styled from "styled-components"
const Title = styled.h1`
color: red;
font-size: 30px;
font-weight: bold;
`
function App() {
return <Title>سلام دنیا</Title>
}در اینجا:
- ا
styled.h1یعنی ساختن یکh1استایلدهیشده - داخل
`(template literal) کدهای CSS معمولی مینویسیم
استفاده از props برای استایل داینامیک
یکی از قدرتهای اصلی Styled Components این است که میتوانیم از props برای تغییر استایل استفاده کنیم.
const Button = styled.button`
background-color: ${props => props.primary ? "blue" : "gray"};
color: white;
padding: 10px;
border: none;
`
function App() {
return (
<>
<Button primary>دکمه اصلی</Button>
<Button>دکمه معمولی</Button>
</>
)
}✅ اگر primary ارسال شود دکمه آبی میشود، در غیر این صورت خاکستری.
گسترش (Extend) کردن استایلها
میتوان یک کامپوننت را بر اساس کامپوننت دیگر ساخت:
const Button = styled.button`
padding: 10px;
border: none;
`
const DangerButton = styled(Button)`
background-color: red;
color: white;
`استایل بر اساس props عددی یا رشتهای
const Box = styled.div`
width: ${props => props.size}px;
height: ${props => props.size}px;
background-color: green;
`
function App() {
return <Box size={100} />
}مزایا
- ✅ استایلها scoped هستند (تداخل ندارند)
- ✅ امکان استایل داینامیک با props
- ✅ همه چیز کنار کامپوننت است
- ✅ مناسب پروژههای متوسط و بزرگ
تفاوت با CSS معمولی
| CSS معمولی | Styled Components |
|---|---|
| فایل جدا | داخل فایل JS |
| احتمال تداخل کلاسها | بدون تداخل |
| استایل داینامیک سختتر | بسیار ساده با props |
جمعبندی
کتابخانه Styled Components:
- کد CSS را داخل JS مینویسد
- کامپوننت استایلدهیشده میسازد
- از props برای تغییر استایل پشتیبانی میکند
- باعث جلوگیری از تداخل استایلها میشود
استایلدهی به روش Tailwind CSS
ابزار Tailwind CSS یک فریمورک CSS است که به جای نوشتن CSS زیاد، از کلاسهای آماده داخل JSX استفاده میکند. یعنی بهجای اینکه خودت CSS بنویسی، کلاسها را مستقیم روی عنصر میگذاری.
مزاياى استفاده از Tailwind CSS
- این ابزار شامل مجموعهای از كلاس هاى CSS است كه هريک عملكرد خاصى را اعمال می كنند، مثل text-center (مركز كردن متن)، 500-bg-blue (رنك پسزمينه آبی).
- كاهش نياز به CSS سفارشى: Tailwind CSS به دليل دارا بودن تعداد زیادی از كلاسهای آماده، نياز به نوشتن CSS سفارشى را به حداقل میرساند.
- قابليت ريسيانسيو وشخصى سازى بالا: با استفاده از پيشوندهاى ريسپانسيو مثل
:smو:mdو:gامى توان رفتار المانها را در دستگاههاى مختلف كنترل كرد. - كاهش حجم فايل CSS در توليد نهايى: با Tailwind می توان كدهای CSSغيرضروری را حذف كرد. در نهايت فقط كلاس هايى كه واقعاً در پروژه استفاده شده اند، باقى مى مانند.
نصب و استفاده
1. نصب کتابخانه Tailwind CSS:
npm install -D tailwindcss postcss autoprefixer2. راه اندازی:
npx tailwindcss init -pسپس این دو خط را به فایل tailwind.config.js اضافه کنید.
"./index.html",
"./src/ ** / *. {js,ts,jsx,tsx}",به این صورت:
/ ** @type {import('tailwindcss').Config} */
export default {
content : [
"./index.html",
"./src/ ** / *. {js,ts,jsx,tsx}",
],
theme: {
extend: {},
},
plugins: [],
}این کار باعث میشود فقط کلاس های استفاده شده را در خروجی نگه دارد، و همین باعث برنامه شما خیلی بهینه تر شود.
3. استفاده و Import در فایل اصلی CSS پروژه:
و در نهایت این چند خط را به ابتدای فایل index.css اضافه کنید:
@tailwind base;
@tailwind components;
@tailwind utilities;ویژگی اصلی Tailwind
در Tailwind، استایلدهی با utility classes انجام میشود.
هر کلاس فقط یک کار کوچک انجام میدهد.
مثلاً:
<div className="p-4 bg-blue-500 text-white rounded-lg hover:bg-red-500">
محتوا
</div>p-4→ paddingbg-blue-500→ پسزمینه آبیtext-white→ متن سفیدrounded-lg→ گوشههای گردhover:bg-red-500→ پسزمینه قرمز (در زمان هاور)
ابزار Tailwind CSS کلاس های زیادی برای شخصی سازی دارد. که شما میتوانید در صفحه رسمی Tailwind CSS به انها دسترسی داشته باشد.
