مقایسه state و ref در React

در این مقاله قصد داریم تا state و ref در React را به طور کامل بررسی کرده و باهم مقایسه نماییم. همچنین بررسی می‌کنیم که با توجه به سناریوهای مختلف، استفاده از کدام یک می‌تواند یک انتخاب منطقی باشد.

هنگامی که در برنامه React خود با نیاز به ذخیره کردن داده‌ها مواجه می‌شویم، اولین سوالی که باید به آن پاسخ دهیم این است که آیا داده‌ها در طول دوره‌ای از چرخه عمر کامپوننت تغییر خواهند کرد؟ اگر تغییری اتفاق نیفتد، تعریف یک متغیر

const
const معمولی مناسب است. اما اگر داده‌ها تغییر کنند، این نقطه همان جایی است که هوک‌های
useState
useState و
useRef
useRef مطرح می‌شوند.

بررسی هوک‌های useState و useRef

هوک useState

هوک

useState
useState برای مدیریت state یک کامپوننت طراحی شده است که نشان دهنده داده‌هایی می‌باشد که می‌توانند در طول زمان تغییر کنند و برای رندر کامپوننت، داده‌های مهمی به حساب می‌آیند. می‌توانیم هوک
useState
useState را از React ایمپورت کرده و state را به کامپوننت خود اضافه نماییم.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
import { useState } from 'react';
import { useState } from 'react';
import { useState } from 'react';

هوک

useState
useState معمولاً با یک value مقداردهی اولیه می‌شود و آرایه‌ای از متغیر state تعریف شده و تابع تنظیم کننده مربوط به آن را return می‌کند. به این صورت که:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
import { useState } from "react";
function App() {
const [count, setCount] = useState(0); //declared useState hook
return (
<>
<h1>State example</h1>
<div>
<button onClick={() => setCount((count) => count + 1)}>
count is {count}
</button>
</div>
</>
);
}
export default App;
import { useState } from "react"; function App() { const [count, setCount] = useState(0); //declared useState hook return ( <> <h1>State example</h1> <div> <button onClick={() => setCount((count) => count + 1)}> count is {count} </button> </div> </> ); } export default App;
import { useState } from "react";

function App() {
  const [count, setCount] = useState(0); //declared useState hook
  
  return (
    <>
      <h1>State example</h1>
      <div>
        <button onClick={() => setCount((count) => count + 1)}>
          count is {count}
        </button>
      </div>
    </>
  );
}
export default App;

در قطعه کد بالا:

  1. useState
    useState با مقدار ۰ مقداردهی اولیه می‌شود و یک متغیر
    count
    count و تابع
    setCount
    setCount را return می‌کند.
  2. متغیر
    count
    count به صورت داینامیک توسط تابع
    setCount
    setCount تنظیم می‌شود که
    count
    count را ۱ واحد افزایش می‌دهد.
  3. هر بار که بر روی دکمه کلیک می‌کنیم، کامپوننت
    App
    App دوباره رندر می‌شود و مقدار به‌روزرسانی شده را در متن دکمه به نمایش می‌گذارد.

درک درست مفهوم state ری‌اکت بسیار مهم است زیرا، یکی از پرکاربردترین مفاهیم می‌باشد.

هوک useRef

هوک

useRef
useRef برای ایجاد ref در کامپوننت‌های React استفاده می‌شود. ref یک آبجکت با ویژگی
current
current است که دارای یک value می‌باشد و اساسا،ً به یک المنت DOM یا نمونه‌ای از یک کامپوننت ارجاع می‌دهد. ما می‌توانیم با دسترسی به ویژگی
current
current، value آن را بخوانیم و به‌روزرسانی کنیم.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
const ref = useRef(initialValue)
ref.current = initialValue
const ref = useRef(initialValue) ref.current = initialValue
const ref = useRef(initialValue)

ref.current = initialValue

در ادامه، یک قطعه کد کامل از ref در یک مثال عملی داریم:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
import { useRef } from "react";
function App() {
let ref = useRef(0);
function handleIncrease() {
ref.current++;
alert(`You have clicked it ${ref.current} times`);
}
return (
<>
<h1>Ref example</h1>
<div>
<button onClick={handleIncrease}>Click Me</button>
</div>
</>
);
}
export default App;
import { useRef } from "react"; function App() { let ref = useRef(0); function handleIncrease() { ref.current++; alert(`You have clicked it ${ref.current} times`); } return ( <> <h1>Ref example</h1> <div> <button onClick={handleIncrease}>Click Me</button> </div> </> ); } export default App;
import { useRef } from "react";

function App() {
  let ref = useRef(0); 
  
  function handleIncrease() {
    ref.current++;
    alert(`You have clicked it ${ref.current} times`);
  }
  return (
    <>
      <h1>Ref example</h1>
      <div>
        <button onClick={handleIncrease}>Click Me</button>
      </div>
    </>
  );
}

export default App;

در قطعه کد بالا:

  1. ابتدا
    userRef
    userRef را از ری‌اکت import می‌کنیم.
  2. سپس در کامپوننت
    App
    App، یک آبجکت ref با مقدار اولیه ۰ تعریف می‌کنیم.
  3. handleIncrease
    handleIncrease تابع کنترل کننده ما است که مقدار
    ref.current
    ref.current را ۱ واحد افزایش داده و سپس در قالب alert، مقدار فعلی را به کاربر نمایش می‌دهد.
  4. در JSX کامپوننت
    App
    App، یک دکمه با prop
    onClick
    onClick داریم و تابع handler
    handleIncrease
    handleIncrease را به آن پاس می‌دهیم.

اکنون که با نحوه کارکرد این دو هوک آشنا شدیم، در ادامه به مقایسه و بررسی اینکه چه زمانی برای استفاده مناسب هستند، خواهیم پرداخت.

مقایسه state و ref در React

Render Trigger

در ری‌اکت، stateها همیشه به دلیل مکانیزمی به نام

reconciliation
reconciliation، که رابط کاربری را بر اساس تغییرات ایجاد شده در state یا props به‌روزرسانی می‌کند، باعث ایجاد یک رندر مجدد می‌شوند.

React در پس‌زمینه، state جدید را با state قبلی مقایسه می‌کند و حداقل تغییرات مورد نیاز برای به‌روزرسانی رابط کاربری که state جدید را منعکس می‌کند، محاسبه می‌نماید. این فرآیند، سازگاری با state یا propsهایی که تغییری در آن‌ها صورت گرفته است را تضمین می‌کند.

از طرف دیگر، هنگامی که تغییراتی در رابط کاربری ایجاد می‌شود refها باعث رندر مجدد نمی‌شوند. زیرا، آن‌ها مستقیماً به چرخه رندر کامپوننت مرتبط نمی‌باشند.

بنابراین، اگر یک رابط کاربری ثابت می‌خواهیم که به تغییرات داده‌ها واکنش نشان دهد، توصیه می‌شود از state استفاده کنیم. اما برای مدیریت مقادیر قابل تغییر بدون تأثیر بر روی رابط کاربری، بهتر است از ref استفاده نماییم.

تغییرپذیری

ما نمی‌توانیم state را مستقیما بعد از تنظیم تغییر دهیم زیرا، تابع setter است که state را به‌روزرسانی می‌کند. با استفاده از این رویکرد، React قابلیت پیش‌بینی و ثبات جریان داده را حفظ می‌کند. این موضوع همچنین باعث می‌شود تا فرآیند دیباگ کردن هم آسان‌تر شود.

برعکس، ref قابل تغییر است زیرا می‌توانیم مقدار

ref current
ref current را خارج از فرآیند رندر، تغییر دهیم. برخلاف state، ما می‌توانیم مقادیر را در هر نقطه‌ای که می‌خواهیم تغییر دهیم، زیرا refها تابع به‌روزرسانی ندارند.

عملیات Read و Write

تابع setter هوک

useState
useState این امکان را به ما می‌دهد تا مقدار state را به‌روزرسانی کنیم. برای مثال:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
const [state, setState] = useState(false)
function handleOpposite(){
setState(!state)
}
const [state, setState] = useState(false) function handleOpposite(){ setState(!state) }
const [state, setState] = useState(false)
function handleOpposite(){
  setState(!state)
 }

در مثال بالا:

  1. مقدار اولیه روی مقدار بولین
    false
    false تنظیم شده است.
  2. تابع
    handleOpposite
    handleOpposite مقدار بولین
    state
    state را نقض کرده و
    setState
    setState مقدار به‌روزرسانی شده
    true
    true را در خود جای می‌دهد.

در این عملیات ساده،

  1. یک عملیات read ضمنی انجام شده است زیرا، قبل از نقیض کردن مقدار، باید به مقدار اولیه دسترسی داشته باشیم.
  2. زمانی که
    !
    ! بر روی مقدار اولیه اعمال می‌شود، عملیات write رخ می‌دهد که مقدار موجود را به مقدار نقیض آن تغییر می‌دهد.

یک عملیات read صریح state زمانی اتفاق می‌افتد که ما به سادگی مستقیماً به متغیر state در JSX یک کامپوننت دسترسی داشته باشیم. برای مثال:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<button onClick={() => setCount((count) => count + 1)}>
count is {count}
</button>
<button onClick={() => setCount((count) => count + 1)}> count is {count} </button>
<button onClick={() => setCount((count) => count + 1)}>
  count is {count}
 </button>

{count}
{count} مقداری است که در حال حاضر به آن دسترسی داریم و بر این اساس در رابط کاربری نمایش داده می‌شود.

از طرف دیگر، دسترسی یا تغییر مقدار فعلی یک

ref
ref در طول rendering می‌تواند در فرآیند تطبیق React اختلال ایجاد کند و به طور بالقوه باعث ایجاد ناسازگاری بین DOM مجازی و DOM واقعی شود.

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

تداوم در رندرها

ماندگاری داده در بین رندرها در React به این معنی است که داده‌ها بین چرخه‌های مختلف رندر یک کامپوننت، ثابت و در دسترس باقی می‌مانند. به این ترتیب داده‌ها هستند، بدون تغییر باقی می‌مانند و پس از رندر مجدد قابل دسترسی می‌باشند. state و ref در React هر دو داده‌ها را در سراسر رندرها حفظ می‌کنند.

ماندگاری برای حفظ یکپارچگی state برنامه بسیار مهم است و تضمین می‌کند که کامپوننت‌ها مطابق انتظار عمل می‌کنند.

به‌روزرسانی‌های Asynchronous

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

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

جمع‌بندی

در این مقاله سعی کردیم تا با مفهوم state و ref و همینطور هوک‌های

useState
useState و
useRef
useRef، که داده‌های داینامیک را در برنامه‌های React مدیریت می‌کنند، بررسی کنیم. ما هر دو هوک را با هم مقایسه کردیم و با شباهت‌ها تفاوت‌های آن‌ها بیشتر آشنا شویم. همینطور به بررسی موقعیت‌های مناسب برای استفاده از هر کدام از آن‌ها پرداختیم.

دیدگاه‌ها:

افزودن دیدگاه جدید