راهنمای جامع بررسی رفهای ریاکت، با تمرکز بر useRef و createRef. بیاموزید چگونه و چه زمانی از هرکدام برای مدیریت بهینه کامپوننت و دسترسی به DOM در اپلیکیشنهای جهانی استفاده کنید.
رفهای (Refs) ریاکت: ابهامزدایی از useRef در مقابل createRef
در دنیای پویای توسعه ریاکت، مدیریت بهینه وضعیت (state) کامپوننت و تعامل با مدل شیء سند (DOM) بسیار حیاتی است. رفهای ریاکت مکانیزمی برای دسترسی و دستکاری مستقیم عناصر DOM یا کامپوننتهای ریاکت فراهم میکنند. دو روش اصلی برای ایجاد رفها useRef
و createRef
هستند. در حالی که هر دو برای ایجاد رفها به کار میروند، در پیادهسازی و موارد استفاده تفاوت دارند. هدف این راهنما ابهامزدایی از این دو رویکرد است تا مشخص شود چه زمانی و چگونه از هرکدام در پروژههای ریاکت خود، بهویژه هنگام توسعه برای مخاطبان جهانی، به طور مؤثر استفاده کنید.
درک رفهای ریاکت
یک Ref (مخفف reference) قابلیتی در ریاکت است که به شما امکان میدهد مستقیماً به یک گره DOM یا یک کامپوننت ریاکت دسترسی داشته باشید. این قابلیت بهویژه در موارد زیر مفید است:
- دستکاری مستقیم یک عنصر DOM، مانند فوکوس کردن روی یک فیلد ورودی.
- دسترسی به متدها یا پراپرتیهای یک کامپوننت فرزند.
- مدیریت مقادیری که در طول رندرهای مختلف باقی میمانند بدون اینکه باعث رندر مجدد شوند (مشابه متغیرهای نمونه در کامپوننتهای کلاسی).
در حالی که ریاکت رویکردی اعلانی (declarative) را تشویق میکند، که در آن رابط کاربری از طریق state و props مدیریت میشود، موقعیتهایی وجود دارد که دستکاری مستقیم ضروری است. رفها راهی برای پر کردن شکاف بین ماهیت اعلانی ریاکت و عملیات دستوری (imperative) روی DOM فراهم میکنند.
createRef
: رویکرد کامپوننتهای کلاسی
createRef
متدی است که توسط ریاکت ارائه شده است. این متد عمدتاً در کامپوننتهای کلاسی برای ایجاد رفها استفاده میشود. هر بار که یک کامپوننت کلاسی نمونهسازی میشود، createRef
یک شیء رف جدید ایجاد میکند. این تضمین میکند که هر نمونه از کامپوننت، رف منحصربهفرد خود را داشته باشد.
سینتکس و کاربرد
برای استفاده از createRef
، ابتدا یک رف را در کامپوننت کلاسی خود، معمولاً در سازنده (constructor)، تعریف میکنید. سپس، رف را با استفاده از ویژگی ref
به یک عنصر DOM یا یک کامپوننت متصل میکنید.
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.myRef = React.createRef();
}
componentDidMount() {
// دسترسی به عنصر DOM پس از mount شدن کامپوننت
this.myRef.current.focus();
}
render() {
return ;
}
}
در این مثال، this.myRef
با استفاده از React.createRef()
ایجاد میشود. سپس به ویژگی ref
عنصر input اختصاص داده میشود. پس از اینکه کامپوننت mount شد (در componentDidMount
)، میتوانید با استفاده از this.myRef.current
به گره واقعی DOM دسترسی پیدا کرده و عملیاتی روی آن انجام دهید (در این مورد، فوکوس کردن روی ورودی).
مثال: فوکوس کردن روی یک فیلد ورودی
سناریویی را در نظر بگیرید که میخواهید یک فیلد ورودی به طور خودکار هنگام mount شدن کامپوننت فوکوس شود. این یک مورد استفاده رایج برای رفها است، بهویژه در فرمها یا عناصر تعاملی.
class FocusInput extends React.Component {
constructor(props) {
super(props);
this.inputRef = React.createRef();
}
componentDidMount() {
this.inputRef.current.focus();
}
render() {
return (
);
}
}
در این مثال، FocusInput
بلافاصله پس از mount شدن، روی فیلد ورودی فوکوس میکند. این کار میتواند با هدایت توجه کاربر به عنصر ورودی به محض رندر شدن کامپوننت، تجربه کاربری را بهبود بخشد.
ملاحظات مهم در مورد createRef
- فقط برای کامپوننتهای کلاسی:
createRef
برای استفاده در کامپوننتهای کلاسی طراحی شده است. اگرچه ممکن است از نظر فنی در کامپوننتهای تابعی نیز کار کند، اما این کاربرد مورد نظر نیست و میتواند منجر به رفتار غیرمنتظره شود. - رف جدید در هر نمونه: هر نمونه از یک کامپوننت کلاسی،
createRef
خود را دریافت میکند. این امر برای حفظ جداسازی بین نمونههای کامپوننت مهم است.
useRef
: هوک کامپوننتهای تابعی
useRef
یک هوک است که در ریاکت نسخه ۱۶.۸ معرفی شد. این هوک راهی برای ایجاد اشیاء رف قابل تغییر (mutable) در کامپوننتهای تابعی فراهم میکند. برخلاف createRef
، هوک useRef
در هر بار رندر شدن کامپوننت، همان شیء رف را برمیگرداند. این ویژگی آن را برای نگهداری مقادیر بین رندرها بدون ایجاد رندر مجدد، ایدهآل میسازد.
سینتکس و کاربرد
استفاده از useRef
ساده است. شما هوک useRef
را با ارسال یک مقدار اولیه فراخوانی میکنید. این هوک یک شیء با پراپرتی .current
برمیگرداند که میتوانید از آن برای دسترسی و تغییر مقدار استفاده کنید.
import React, { useRef, useEffect } from 'react';
function MyFunctionalComponent() {
const myRef = useRef(null);
useEffect(() => {
// دسترسی به عنصر DOM پس از mount شدن کامپوننت
if (myRef.current) {
myRef.current.focus();
}
}, []);
return ;
}
در این مثال، useRef(null)
یک رف با مقدار اولیه null
ایجاد میکند. هوک useEffect
برای دسترسی به عنصر DOM پس از mount شدن کامپوننت استفاده میشود. پراپرتی myRef.current
مرجع به عنصر input را نگه میدارد و به شما امکان میدهد روی آن فوکوس کنید.
مثال: ردیابی مقادیر قبلی prop
یکی از کاربردهای قدرتمند useRef
، ردیابی مقدار قبلی یک prop است. از آنجایی که تغییرات در رفها باعث رندر مجدد نمیشوند، میتوانید از آنها برای ذخیره مقادیری که میخواهید در طول رندرها حفظ شوند بدون تأثیر بر رابط کاربری، استفاده کنید.
import React, { useRef, useEffect } from 'react';
function PreviousValueComponent({ value }) {
const previousValue = useRef();
useEffect(() => {
previousValue.current = value;
}, [value]);
return (
مقدار فعلی: {value}
مقدار قبلی: {previousValue.current}
);
}
در این مثال، previousValue.current
مقدار قبلی prop با نام value
را ذخیره میکند. هوک useEffect
هر زمان که prop با نام value
تغییر کند، رف را بهروزرسانی میکند. این به شما امکان میدهد مقادیر فعلی و قبلی را مقایسه کنید، که میتواند برای تشخیص تغییرات یا پیادهسازی انیمیشنها مفید باشد.
ملاحظات مهم در مورد useRef
- فقط برای کامپوننتهای تابعی:
useRef
یک هوک است و فقط میتواند در کامپوننتهای تابعی یا هوکهای سفارشی استفاده شود. - ماندگاری بین رندرها: هوک
useRef
در هر رندر همان شیء رف را برمیگرداند. این ویژگی کلیدی توانایی آن در حفظ مقادیر بدون ایجاد رندر مجدد است. - پراپرتی
.current
قابل تغییر: شما میتوانید مستقیماً پراپرتی.current
شیء رف را تغییر دهید. - مقدار اولیه: میتوانید یک مقدار اولیه به
useRef
بدهید. این مقدار در اولین رندر کامپوننت به پراپرتی.current
اختصاص داده میشود. - بدون رندر مجدد: تغییر پراپرتی
.current
یک رف باعث رندر مجدد کامپوننت نمیشود.
useRef
در مقابل createRef
: مقایسه دقیق
اکنون که هر دو useRef
و createRef
را به صورت جداگانه بررسی کردیم، بیایید آنها را کنار هم مقایسه کنیم تا تفاوتهای کلیدی آنها و زمان انتخاب یکی بر دیگری را برجسته کنیم.
ویژگی | useRef |
createRef |
---|---|---|
نوع کامپوننت | کامپوننتهای تابعی | کامپوننتهای کلاسی |
هوک یا متد | هوک | متد |
نمونه رف | در هر رندر همان شیء رف را برمیگرداند | در هر نمونه از کامپوننت یک شیء رف جدید ایجاد میکند |
موارد استفاده |
|
|
انتخاب رف مناسب: راهنمای تصمیمگیری
در اینجا یک راهنمای ساده برای کمک به شما در انتخاب بین useRef
و createRef
آورده شده است:
- آیا با یک کامپوننت تابعی کار میکنید؟ از
useRef
استفاده کنید. - آیا با یک کامپوننت کلاسی کار میکنید؟ از
createRef
استفاده کنید. - آیا نیاز به حفظ یک مقدار در طول رندرها بدون ایجاد رندر مجدد دارید؟ از
useRef
استفاده کنید. - آیا نیاز به ردیابی مقدار قبلی یک prop دارید؟ از
useRef
استفاده کنید.
فراتر از دستکاری DOM: موارد استفاده پیشرفته برای رفها
در حالی که دسترسی و دستکاری عناصر DOM یک کاربرد اصلی برای رفها است، آنها امکاناتی فراتر از این عملکرد اصلی ارائه میدهند. بیایید برخی از سناریوهای پیشرفته را که در آنها رفها میتوانند بهویژه مفید باشند، بررسی کنیم.
۱. دسترسی به متدهای کامپوننت فرزند
از رفها میتوان برای دسترسی به متدهای تعریفشده در کامپوننتهای فرزند استفاده کرد. این به یک کامپوننت والد اجازه میدهد تا به طور مستقیم اقداماتی را در فرزندان خود فعال کرده یا دادههایی را از آنها بازیابی کند. این رویکرد بهویژه زمانی مفید است که به کنترل دقیق بر کامپوننتهای فرزند نیاز دارید.
class ParentComponent extends React.Component {
constructor(props) {
super(props);
this.childRef = React.createRef();
}
handleClick = () => {
// فراخوانی یک متد در کامپوننت فرزند
this.childRef.current.doSomething();
};
render() {
return (
);
}
}
class ChildComponent extends React.Component {
doSomething = () => {
console.log('اکشن کامپوننت فرزند اجرا شد!');
};
render() {
return این یک کامپوننت فرزند است.;
}
}
در این مثال، ParentComponent
از یک رف برای دسترسی به ChildComponent
و فراخوانی متد doSomething
آن استفاده میکند.
۲. مدیریت فوکوس و انتخاب
رفها برای مدیریت فوکوس و انتخاب در فیلدهای ورودی و سایر عناصر تعاملی بسیار ارزشمند هستند. این برای ایجاد رابطهای کاربری قابل دسترس و کاربرپسند بسیار مهم است.
import React, { useRef, useEffect } from 'react';
function FocusOnMount() {
const inputRef = useRef(null);
useEffect(() => {
if (inputRef.current) {
inputRef.current.focus();
inputRef.current.select(); // انتخاب متن در ورودی
}
}, []);
return ;
}
این مثال به محض mount شدن کامپوننت، روی ورودی فوکوس کرده و متن آن را انتخاب میکند.
۳. متحرکسازی عناصر
رفها میتوانند در ترکیب با کتابخانههای انیمیشن (مانند GreenSock یا Framer Motion) برای دستکاری مستقیم DOM و ایجاد انیمیشنهای پیچیده استفاده شوند. این امکان کنترل دقیق بر توالیهای انیمیشن را فراهم میکند.
مثال با استفاده از جاوا اسکریپت خالص برای سادگی:
import React, { useRef, useEffect } from 'react';
function AnimatedBox() {
const boxRef = useRef(null);
useEffect(() => {
const box = boxRef.current;
if (box) {
// انیمیشن ساده: حرکت جعبه به سمت راست
box.animate(
[
{ transform: 'translateX(0)' },
{ transform: 'translateX(100px)' },
],
{
duration: 1000, // ۱ ثانیه
iterations: Infinity, // تکرار بی نهایت
direction: 'alternate',
}
);
}
}, []);
return ;
}
این مثال از Web Animations API برای متحرکسازی یک جعبه ساده استفاده میکند و آن را به صورت افقی به عقب و جلو حرکت میدهد.
بهترین شیوهها برای استفاده از رفهای ریاکت در اپلیکیشنهای جهانی
هنگام توسعه اپلیکیشنهای ریاکت برای مخاطبان جهانی، مهم است که در نظر بگیرید چگونه رفها با بینالمللیسازی (i18n) و محلیسازی (l10n) تعامل دارند. در اینجا برخی از بهترین شیوهها آورده شده است:
۱. دسترسیپذیری (A11y)
اطمینان حاصل کنید که استفاده شما از رفها تأثیر منفی بر دسترسیپذیری نداشته باشد. به عنوان مثال، هنگام فوکوس کردن برنامهریزی شده روی عناصر، ترتیب فوکوس کاربر را در نظر بگیرید و اینکه آیا تغییر فوکوس برای صفحهخوانها و کاربران کیبورد مناسب است یا خیر.
import React, { useRef, useEffect } from 'react';
function AccessibleFocus() {
const buttonRef = useRef(null);
useEffect(() => {
const button = buttonRef.current;
if (button) {
// فقط در صورتی فوکوس کنید که دکمه قبلاً توسط کاربر فوکوس نشده باشد
if (document.activeElement !== button) {
button.focus();
}
}
}, []);
return ;
}
۲. فیلدهای ورودی بینالمللی
هنگام کار با فیلدهای ورودی، به روشهای مختلف ورودی و مجموعههای کاراکتر مورد استفاده در زبانهای مختلف توجه داشته باشید. اطمینان حاصل کنید که دستکاریهای مبتنی بر رف شما (مانند انتخاب، موقعیت مکاننما) در انواع ورودی و زبانهای مختلف به درستی کار میکنند. کامپوننتهای خود را با زبانها و روشهای ورودی مختلف به طور کامل آزمایش کنید.
۳. طرحبندیهای راست به چپ (RTL)
اگر اپلیکیشن شما از زبانهای راست به چپ (مانند عربی، عبری) پشتیبانی میکند، اطمینان حاصل کنید که دستکاریهای DOM شما با استفاده از رفها، طرحبندی معکوس را در نظر میگیرند. به عنوان مثال، هنگام متحرکسازی عناصر، جهت انیمیشن را برای زبانهای RTL معکوس کنید.
۴. ملاحظات عملکرد
در حالی که رفها راهی قدرتمند برای تعامل با DOM فراهم میکنند، استفاده بیش از حد میتواند منجر به مشکلات عملکردی شود. دستکاری مستقیم DOM، DOM مجازی و فرآیند تطبیق ریاکت را دور میزند و به طور بالقوه منجر به ناهماهنگیها و بهروزرسانیهای کندتر میشود. از رفها با احتیاط و فقط در مواقع ضروری استفاده کنید.
نتیجهگیری
رفهای ریاکت، بهویژه useRef
و createRef
، ابزارهای ضروری برای توسعهدهندگان ریاکت هستند. درک تفاوتهای ظریف هر رویکرد و زمان استفاده مؤثر از آنها برای ساخت اپلیکیشنهای قوی و کارآمد بسیار مهم است. createRef
استاندارد مدیریت رفها در کامپوننتهای کلاسی باقی میماند و تضمین میکند که هر نمونه، رف منحصربهفرد خود را دارد. useRef
با ماهیت ماندگار خود در طول رندرها، برای کامپوننتهای تابعی ایدهآل است و راهی برای مدیریت عناصر DOM و حفظ مقادیر بدون ایجاد رندرهای غیرضروری ارائه میدهد. با استفاده هوشمندانه از این ابزارها، میتوانید عملکرد و تجربه کاربری اپلیکیشنهای ریاکت خود را بهبود بخشیده و با رابطهای کاربری قابل دسترس و کارآمد، به مخاطبان جهانی خدمات ارائه دهید.
همچنان که ریاکت به تکامل خود ادامه میدهد، تسلط بر این مفاهیم بنیادی شما را قادر میسازد تا تجربیات وب نوآورانه و کاربرپسندی ایجاد کنید که از مرزهای جغرافیایی و فرهنگی فراتر میروند. به یاد داشته باشید که دسترسیپذیری، بینالمللیسازی و عملکرد را برای ارائه اپلیکیشنهای واقعاً جهانی در اولویت قرار دهید.