10
اشتراک
مخزن

آپدیت react 18 و اضافه شدن هوک جذاب ()useTransition

مهمترین اتفاق در آپدیت react 18 قطعا Concurrency خواهد بود و ما میتونیم با استفده از این هوک کنترلی نسبی بر ترتیب اجرا شدن رویداد ها و توابع داشته باشیم و این قدمی هست برای response بودن برنامه ما و باعث خواهد شد که در حین انجام یک عملیات سنگین برنامه ما نسبت به عملیات های کاربر بی پاسخ نماند .

از اونجا که React بروی یک threaed اجرا می شود و باید همه ی events ,functions هارو مرتب و اولویت بندی کند توسعه دهنده تا قبل از React18 کنترل بر روی اولویت انجام آن ها نداشت و نهایت توان این بود که تابع رو به یک debuncer بسپاریم تا در performance برنامه به ما کمک کند. ولی با آپدیت جدید این hook به کمک ما اومد تا ما بتونیم با سپردن امور expensive خودمون به عنوان یک callback اون رو در اولویت های بعدی برای انجام گرفتن قرار بدهیم. حال بریم با یک مثال مفهوم رو بیشتر برای خودمون جا بندازیم.

شرح مثالی که قرار است انجام بدیم

دراینجا ما یک input خواهیم داشت و در onChange اون یک handler قرار خواهیم داد که در اون دو کار انجام خواهد شد اولی setValue کردن مقدار input و پاس دادن مقداد آن به value input تا با کمک اون بتونیم از میزان response بودن برنامه مطلع بشیم . در دومی ما حلقه ای خواهیم زد که با هر بار تغییر در مقدار input به تعداد خیلی زیادی object به اسم item دردرون یک state ذخیره و در پایین input روی این مقدار map بزنیم و قابل نمایش باشد . هر مقدار که تعداد حلقه ما بیشتر شود اون عملیات expensive تر خواهد بود و برنامه دارای lag و گاها باعث crash خواهد شد .

تست کردن این عملیات بدون ()useTransition

1import { useState } from "react";
2import styles from "../styles/Home.module.css";
3
4export default function Home() {
5  const [value, setValue] = useState(0);
6  const [list, setList] = useState([]);
7
8  const handler = (e) => {
9    const getValue = e.target.value;
10    setValue(getValue);
11    const numberList = [];
12    const item = {
13      title: "useTransition",
14      imageUrl: "/images/lag.png",
15    };
16    for (let i = 0; i <= 10000; i++) {
17      numberList.push(item);
18    }
19    setList(numberList);
20  };
21  return (
22    <div className={styles.container}>
23      <input type="number" value={value} onChange={handler} />
24      <ul className={styles.list}>
25        {list.map((item, index) => (
26          <li key={index}>
27            <img src={item.imageUrl} />
28            <span>{item.title}</span>
29          </li>
30        ))}
31      </ul>
32    </div>
33  );
34}

حالا می تونیم نتیجه کد های بالا رو در پایین مشاهده کنیم و می بینیم که در زمان تغییر مقدار input برنامه به دلیل انجام دو عملیاتی که در بالا توضیح دادم با هر تغیری که در input اتفاق می افتد انجام خواهد شد و کنترلی بروی اولویت انجام این دو نخواهیم داشت به همین علت برنامه به دلیل انجام حلقه ای که وجود دار عملیات expensive انجام خواهد داد و به سختی می تواند به مابقی عملیات های کاربر پاسخ گو باشد .

تست کردن این عملیات با ()useTransition

خب حالا بریم بیشتر با syntax خود این hook آشنا بشیم ابتدا مثل بقیه hook ها از React import خواهیم کرد و سپس برای استفاده از اون به این شیوه عمل خواهیم کرد :

1const [isPending, startTransition] = useTransition();

در اینجا یک مقدار isPending خواهیم داشت که شما می تونید هر اسمی دیگری استفاده کنید وهمون طور که از اسمش میشه فهمید به ما bolean را برمیگرداند که ما رو از تکمیل انجام عملیاتی که درون startTransition خواهد افتاد رو مطلع خواهد کرد و در نهایت در کد هامون جایی که map انجام خواهد شد یک ternary operator با کمک isPending خواهیم داشت که در زمان calculate کردن برنامه بتوانیم برای UX بهتر به کاربر اعلام انتظار برای مشاهده نتایج بدهیم. و در نهایت به اصل مبحث خواهیم رسید جایی که باید امور expensive رو به startTransition به صورت یک fallback بسپاریم و مانند کد پایین از اون استفاده کنیم .

1startTransition(() => {
2
3  // operation expensive
4  // for example : 
5   // Request a heavy filter from the server
6  
7});
8

حالا وقتشه که کامپوننت رو به روز کنیم و startTransition رو بهش اضافه کنیم .

1import { useState, useTransition } from "react";
2import styles from "../styles/Home.module.css";
3
4export default function Home() {
5
6  const [isPending, startTransition] = useTransition();
7  const [value, setValue] = useState(0);
8  const [list, setList] = useState([]);
9
10  const handler = (e) => {
11    const getValue = e.target.value;
12    setValue(getValue);
13
14    startTransition(() => {
15      const numberList = [];
16      const item = {
17        title: "useTransition",
18        imageUrl: "/images/no-Lag.jpg",
19      };
20      for (let i = 0; i <= 10000; i++) {
21        numberList.push(item);
22      }
23      setList(numberList);
24    });
25    
26  };
27  return (
28    <div className={styles.container}>
29      <input type="number" value={value} onChange={handler} />
30      <ul className={styles.list}>
31        {!isPending ? list.map((item, index) => (
32          <li key={index}>
33            <img src={item.imageUrl} />
34            <span>{item.title}</span>
35          </li>
36        )) : <span class="spinner"></span>}
37      </ul>
38    </div>
39  );
40}

و نتیجه رو میتونیم در تصویر پایین ببینیم و ما با کمک startTransition تونستیم کارهای expensive در اولویت های بعدی قرار بدهیم و setValue که برای ما الویت بیشتری دارد انجام شده و ما مقدار value رو می تونیم در داخل input بعد از هر تماس کاربر برای تغییر اون مقدار ببینیم و برنامه ما کاملا responsive خواهد بود .