بررسی تغییرات ReactJS 19 - در نسخه جدید ری اکت چخبره ؟

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

وقتی جامعه برنامه نویس های ری اکت نیازی را حس میکنند و درخواستی را به سمت تیم سازنده ارسال میکنند، تیم سازنده به خوبی اونهارو میبینه و درصورت لزوم هم دست به کار میشه.

اتفاقات جدیدی که قرار در ری اکت 19 بیوفته واقعا جذاب و دوست داشتنین و اگر در یک جمله بخوام این بروزرسانی را توصیف کنم باید بگم " کد کمتر، قدرت بیشتر ".

در این مقاله هم قراره بطور کامل نسخه جدید ری اکت یعنی ReactJS 19 را باهم برسی کنیم و ببینیم چه تغییرات و ویژگی های جدیدی را در این ورژن خواهیم داشت. توجه داشته باشید که این ورژن هنوز بطور رسمی منتشر نشده و ما این مقاله را طبق آخرین اطلاعیه ها و اخبار موجود نوشتیم، بنابر این گوش به زنگ آخرین آپدیت های دنیای وب باشید تا به محض انتشار رسمی، بطور کامل تر و دقیق تر با ری اکت 19 آشنا بشید.

بررسی امکانات جدید در ری اکت 19

خیلی سریع و خلاصه بریم اول یک نگاهی به همه چیزهای جدیدی که قراره تو این ورژن داشته باشیم بندازیم و بعد هم بریم سراغ بررسی تک تک اونها:

  • React compiler: تیم ری اکت هم اکنون داره برروی کامپایلر جدید خودش کار میکنه و قراره اونرو در ری اکت 19 داشته باشیم. هم اکنون اپلیکیشن اینستاگرام داره از این کامپایلر جدید استفاده میکنه.
  • Server components: بالاخره بعد از سالها توسعه، ری اکت سرور کامپوننت هارو معرفی کرد و در react 19 قراره اونرو داشته باشیم. لازم به ذکر که هم اکنون در nextjs و نسخه canary ری اکت سرور کامپوننت هارو داریم.
  • Actions: اکشن ها تحولی بزرگ ایجاد میکنند و باعث میشن قدرتمند تر با DOM Element ها تعامل داشته باشیم.
  • Document Metadata: یک قابلیت دیگه که جای خالیش بشدت حس میشد، امکان نوشتن meta data های دلخواه ما بدون هیچ کد اضافی بود.
  • Assets Loading: این قابلیت به ما اجازه میده تا asset هامونو در background بارگذاری کنیم و در نتیجه سرعت و عملکرد و تجربه کاربری بشدت ارتقا داده میشه.
  • Web components: این هم یکی دیگه از قابلیت های جدید و هیجان انگیز در ری اکت 19 هست. از این به بعد میتونیم وب کامپوننت هارو داخل خود ری اکت بگنجانیم.
  • Hooks: تعدادی هوک جدید هم قراره تو ReactJS 19 داشته باشیم که در ادامه بطور کامل معرفیشون کردیم.

ری اکت 19 قراره با یکی از فرسایشی ترین چالش های خودش، یعنی re-render های اضافی دست و پنجه نرم کنه. مشکلی که سالها برنامه نویس های زیادی درگیرش بودن و به بهانه های مختلف بهش اشاره میکردن.

توسعه دهنده ها معمولا از تکنیک هایی مثل memo, useCallback, useMemo و ... جهت کاهش تعداد این re-render های اضافی استفاده میکردند و در این ورژن دیگه نیازی به این کارها نیست و خود ری اکت در پشت صحنه این کار هارو برای ما انجام میده.

1. React Compiler

در حال حاضر، ری اکت بطور خودکار re-render هایی که بخاطر تغییرات state ها بوجود میان رو مدیریت و بهینه سازی نمیکنه و ما برای انجام اینکار نیاز داریم تا بصورت دستی و بوسیله چیزهایی مثل useMemo, useCallback و memo این re-render های اضافی را مدیریت کنیم.

اما بازخورد هایی که از طرف جامعه برنامه نویسانی که از ری اکت استفاده میکنند اومد، تیم ری اکت را وادار کرد تا چاره ای بی اندیشن و کاری کنند که تمامی این موارد و بهینه سازی ها در پشت صحنه و توسط خود ری اکت انجام بشه و در نتیجه، تصمیم بر ساخت کامپایلری جدید گرفته شد. این کامپایلر جدید تصمیم میگیره که کی state را عوض کنه و چه زمانی هم UI را آپدیت کنه.

در نتیجه دیگه نیازی نیست ما به عنوان توسعه دهنده زیاد نگران مدیریت re-render های اضافی باشیم و این بدان معنا هم هست که دیگه نیازی به استفاده از useMemo, useCallback نخواهیم داشت.

یک چیز جذابی هم که درباره ری اکت و تیم توسعه دهنده اون وجود داره اینه که همیشه آپدیت های جدید، ابتدا برروی پروژه های واقعیشون مثل اپلیکیشن اینستاگرام تست میشه و بعد بصورت عمومی منتشر میشه و این امر باعث میشه تا ما با آپدیت هایی مطمئن و کارامد طرف باشیم.

2. Server Components

در حال حاضر کامپوننت های ری اکتی در سمت client یا همان کاربر رندر و اجرا میشن، اما در ورژن جدید ری اکت، یعنی ری اکت 19 ما قادر خواهیم بود تا کامپوننت هارو در سمت سرور هم رندر کنیم.

ما server side rendering را از خیلی وقت پیش در NextJS داشتیم و این قابلیت اخیر که هر کامپوننتی که مدنظرمون بود را تبدیل به server component بکنیم هم از NextJS 13 به بعد ( دقیقا بعد از اینکه ری اکت این قابلیت را در نسخه canary معرفی کرد ) هم به NextJS اضافه شد و حالا از NextJS 13 به بعد تمام کامپوننت ها بصورت پیشفرض server component هستند.

این قابلیت هم که قرار در ری اکت 19 بصورت رسمی اضافه بشه تا ما بدون نیاز به ابزار یا فریمورک جداگانه ای مثل NextJS بتونیم ازش استفاده کنیم. برخی از مزیت های رندر کامپوننت در سمت سرور میشه به موارد زیر اشاره کرد:

  • SEO: برای اینکه صفحات ما در دسترس ربات های گوگل قرار بگیرند، نیازه تا به محض ورود ربات گوگل به صفحه ما، html اون صفحه در اختیار اون ربات قرار بگیره،در نتیجه رندر کامپوننت در سمت سرور باعث میشه تا اول تمام html اماده بشه و بعد صفحه بارگذاری بشه و در نتیجه به محض بارگذاری صفحه، تمام محتوا در اختیار ربات گوگل قرار میگیره.
  • Performance: سرور کامپوننت ها سرعت بارگذاری اولیه بالاتری دارند و این عملکرد خودش را در صفحات سنگین تر به خوبی نشون میده.
  • اجرای کد در سرور: طبیعتا اولین و مهم ترین مزیت هم اجرای کد در سمت سرور و قبل از رسیدن کار به سمت کاربر هستش. ما میتونیم api کال های مهمی را در سمت سرور داشته باشیم و بعد تصمیم بگیریم در سمت کاربر چه اتفاقی بیوفته!

همانطور که قبل تر هم گفتیم، تمام کامپوننت ها در ری اکت 19 بطور پیشفرض در سمت کاربر اجرا خواهند شد و برای اینکه یک کامپوننت را به سرور کامپوننت تبدیل کنیم، نیاز داریم تا در اولین خط از کامپوننت خودمون عبارت "use server" را بنویسیم.

3. actions

در نسخه 19 ری اکت، یکی دیگه از تغییرات جذاب و کاربردی که داریم action ها هستند و بوسیله این action ها میتونیم با قدرت بیشتری با فرم ها کار کنیم و در این نسخه میتونیم جای را با form action جابجا کنیم.

قبل از actions:

در مثال زیر ما فرمی را داریم که فقط با صدا زده شدن یک event یعنی اجرا میشه و سرچ ما زمانی زده میشه که فرم ما ثبت شده باشه. این مارو به استفاده از event های ری اکتی که در سمت کاربر وجود دارند محدود میکنه و نمیتونیم این فرم را در سمت سرور اجرا کنیم.

<form ={search}>
  <input name=&quotquery&quot />
  <button type=&quotsubmit&quot>Search</button>
</form>

بعد از actions:

با معرفی ری اکت 19 میتونیم فرم اکشن خودمون را بوسیله اتریبیوت action فرم ها و در سمت سرور اجرا کنیم و لازم به ذکر که هم میتونیم بصورت sync و هم بصورت async اینکارو انجام بدیم. هدف هم سادست، مدیریت بهتر state ها و داده ها و ساده سازی ثبت فرم ها.

&quotuse server&quot

const submitData = async (userData) => {
    const newUser = {
        username: userData.get('username'),
        email: userData.get('email')
    }
    console.log(newUser)
}
const Form = () => {
    return <form action={submitData}>
        <div>
            <label>Name</label>
            <input type=&quottext&quot name='username'/>
        </div>
        <div>
            <label>Name</label>
            <input type=&quottext&quot name=&quotemail&quot />
        </div>
        <button type='submit'>Submit</button>
    </form>
}

export default Form;

در کد بالا، submitData در واقع action ما در سرور کامپوننت هست و فرم ما هم کامپوننت سمت کلاینت ما هستش که داره از submitData به عنوان action استفاده میکنه. این submitData در سمت سرور اجرا خواهد شد و این برقراری ارتباط submitData که در سمت سرور وجود داره و فرمی که در سمت کلاینت هست فقط به لطف action ها و در ری اکت 19 ممکن خواهد بود.

در ادامه این مقاله هم قراره با چندتا هوک جدید آشنا بشیم که دست مارو در کار با فرم ها و action ها باز تر میزاره و قدرت بیشتری هم بهمون میده.

4. Web Components

وب کامپوننت ها به ما اجازه میدن تا بوسیله Html, Css و Javascript کامپوننت هایی بسازیم که میشه ازشون مثل یک تگ واقعی و استاندارد html ازش استفاده کرد.

در حال حاضر امکان استفاده از وب کامپوننت ها بصورت مستقیم و بدون استفاده از ابزارهای خارجی وجود نداره و تلفیق وب کامپوننت ها با کامپوننت های ری اکتی هم اصلا کار ساده ای نیست.

خبر خوب اینه که این قضیه موقتیه و با انتشار ری اکت 19 خیلی راحت تر میتونیم وب کامپوننت هارو در کامپوننت های ری اکتی خودمون استفاده کنیم.

فعلا اطلاعات بیشتری از سینتکس و نحوه کار این ویژگی در ری اکت 19 وجود نداره و صرفا امیدواریم که به راحت ترین شکل ممکن این مورد در اختیار ما قرار بگیره 😬

5. Document Metadata

اکثر وبسایت ها به SEO نیاز دارن تا بتونن بهتر در نتایج گوگل ظاهر بشن و مخاطب خودشون را جذب کنند و تا به امروز امکان افزودن متا تگ های مانند title, description و ... بطور مستقیم و بدون نیاز به کدهای اضافی و پکیج های خارجی ممکن نبود و امروز به لطف ری اکت 19 میتونیم به راحتی آب خوردن و بدون هیچ دردسری هر متا تگی که دلمون میخواد را در صفحات خودمون استفاده کنیم.

Const HomePage = () => {
  return (
    <>
      <title>vaspar</title>
      <meta name=&quotdescription&quot content=&quotvaspar blogs&quot />
      // Page content
    </>
  );
}

6. Asset Loading

معمولا در ابتدای کار یک view ساده از صفحه وب ما در مرورگر رندر میشه و سپس بدنبالش stylesheets, fonts, و image ها بارگذاری میشن و این امر باعث میشه که برای چند لحظهui بدون استایل و یا ui که نامرتب و با استایل های نصفه و نیمه هستش را ببینیم. توسعه دهنده ها برای مدیریت این مشکل، کدهای اضافی مینویسن تا بتونن بارگذاری شدن کامل این موارد را تشخیص بدن و بعد از بارگذاری کامل اونهارو به دید کاربر در بیارن ( مثلا یک skeleton loading را به جای یک عکس نمایش میدن )

در ری اکت 19 این فایل ها، عکس ها یا هر نوع دیگه ای از asset ها هم در background بارگذاری میشن و هم دارای lifecycle Suspense هستند و این به آن معناست که بارگذاری شدن asset های ما هم بهینه تر میشن و هم به راحتی میتونیم زمان کامل load شدنشون را تشخیص بدیم.

همچنین یکسری Resource Loading API جدید مثل preload و preinit را هم خواهیم داشت که کنترل بیشتری به ما میده و میتونیم تعیین کنیم که Asset مدنظر ما چه زمان و به چه شکلی بارگذاری بشن.

با بارگذاری شدن asset ها در background و بصورت async هم مدت زمان بارگذاری صفحات ما کوتاه تر میشه و هم با بهم ریختگی اولیه ui مواجه نخواهیم شد و این تجربه خیلی بهتری را به کاربران وبسایت ما میده.

7. هوک های جدید

هوک ها از دوست داشتنی ترین قابلیت های ری اکت هستند و همه ما دفعات خیلی زیادی از Built-in Hook ها و custom hook ها در پروژه های ری اکتی خودمون استفاده کردیم. در ری اکت 19 یکسری از این هوک ها قراره تغییراتی در استفاده ازشون بوجود بیاد و هوک هایی مثل useMemo, forwardRef, useEffect, و useContext قراره پس از معرفی شدن هوک جدید use تغییراتی را داشته باشند.

useMemo

در ری اکت 19 دیگه نیازی به استفاده از useMemo نیست و ری اکت کامپایلر قراره این کار را در پشت صحنه برای ما انجام بده. بریم در ادامه دوتا مثال از قبل ری اکت 19 که مجبور به استفاده از useMemo بودیم و بعدش که دیگه نیازی بهش نداریم را ببینیم:

قبل:

import React, { useState, useMemo } from 'react';

function ExampleComponent() {
  const [inputValue, setInputValue] = useState('');

  // Memoize the result of checking if the input value is empty
  const isInputEmpty = useMemo(() => {
    console.log('Checking if input is empty...');
    return inputValue.trim() === '';
  }, [inputValue]);

  return (
    <div>
      <input
        type=&quottext&quot
        value={inputValue}
        ={(e) => setInputValue(e.target.value)}
        placeholder=&quotType something...&quot
      />
      <p>{isInputEmpty ? 'Input is empty' : 'Input is not empty'}</p>
    </div>
  );
}

export default ExampleComponent;

بعد:

import React, { useState } from 'react';

function ExampleComponent() {
  const [inputValue, setInputValue] = useState('');

  const isInputEmpty = () => {
    console.log('Checking if input is empty...');
    return inputValue.trim() === '';
  });

  return (
    <div>
      <input
        type=&quottext&quot
        value={inputValue}
        ={(e) => setInputValue(e.target.value)}
        placeholder=&quotType something...&quot
      />
      <p>{isInputEmpty ? 'Input is empty' : 'Input is not empty'}</p>
    </div>
  );
}

export default ExampleComponent;

forwardRef

در ری اکت 19 دیگه به forwardRef هم نیازی نیست چراکه میتونیم به راحتی ref مدنظرمون را از طریق props پاس بدیم به کامپوننت مدنظرمون!

قبل:

قبل از react 19 مجبور بودیم که ref هارو به شکل زیر و بوسیله forwardRef به کامپوننت مدنظرمون بدیم:

import React, { forwardRef } from 'react';

const ExampleButton = forwardRef((props, ref) => (
  <button ref={ref}>
    {props.children}
  </button>
));

بعد:

اما در ورژن جدید ری اکت، میتونیم به راحتی ref مدنظر را از طریق props پاس بدیم:

import React from 'react';

const ExampleButton = ({ ref, children }) => (
  <button ref={ref}>
    {children}
  </button>
);

هوک جدید use

در ری اکت 19 یک هوک جدید با نام use خواهیم داشت و این هوک قراره کار با promise ها, کدهای async و context را راحت تر کنه. سینتکس این هوک به شکل زیره:

const value = use(resource);

در مثال زیر هم متونیم یک مثال واقعی تر از یک http get request را بوسیله هوک use ببینیم:

import { use } from &quotreact&quot

const fetchUsers = async () => {
    const res = await fetch('https://jsonplaceholder.typicode.com/users');
    return res.json();
  };
  
  const UsersItems = () => {
    const users = use(fetchUsers());
  
    return (
      <ul>
        {users.map((user) => (
          <div key={user.id} className='bg-blue-50 shadow-md p-4 my-6 rounded-lg'>
            <h2 className='text-xl font-bold'>{user.name}</h2>
            <p>{user.email}</p>
          </div>
        ))}
      </ul>
    );
  }; 
export default UsersItems;

بریم کد بالارو بشکافیم و ببینیم چطور و چکاری را داره انجام میده:

  1. کار فانکشن fetchUsers دریافت لیست کاربران از سروری هست که آدرسش را بهش دادیم.
  2. قبلتر نیاز بود تا فانکشن fetchUsers را در useEffect صدا کتیم و سپس مقدارش را در یک state ذخیره کنیم اما حالا هوک use هردوی این کارهارو برای ما انجام میده.
  3. در نهایت هم که جواب fetchUsers ما در قالب return هوک use که ما اونرو در متغیر users ذخیره کردیم
  4. مزیتی که این هوک به ما میده اینه که میتونیم فانکشن هایی که مسئول کار با api ها هستند را در یک فایل جداگانه ذخیره کنیم و به راحتی و با کدی کمتر و بدون نیاز به state و useEffect اونهارو اجرا کنیم.

برای استفاده از context هم حتی میتونیم از هوک use استفاده کنیم:

const Card = () => {
  // use Hook()
  const { theme, toggleTheme } = use(ThemeContext);

  return (
    <div
      className={`p-4 rounded-md ${
        theme === 'light' ? 'bg-white' : 'bg-gray-800'
      }`}
    >
      <h1
        className={`my-4 text-xl ${
          theme === 'light' ? 'text-gray-800' : 'text-white'
        }`}
      >
        Theme Card
      </h1>
      <p className={theme === 'light' ? 'text-gray-800' : 'text-white'}>
       Hello!! use() hook
      </p>
      <button
        ={toggleTheme}
        className='bg-blue-500 hover:bg-blue-600 text-white rounded-md mt-4 p-4'
      >
        {theme === 'light' ? 'Switch to Dark Mode' : 'Switch to Light Mode'}
      </button>
    </div>
  );
};

هوک جدید useFormStatus

بوسیله این هوک میتونیم کنترل بیشتری برروی آخرین فرم ثبت شده شده خودمون داشته باشیم.

const { pending, data, method, action } = useFormStatus();
  • pending: اطلاع از در حال ثبت بودن یا نبودن فرم.
  • data: یک FormData آبجکت که شامل مقادیر فیلدهایی هست که داخل تگ form ما قرار دارند.
  • method: شامل http method ثبت فرم ما هستش. مثلا GET یا POST بودنش.
  • action: یک رفرنس از فانکشنی که به اتریبیوت action فرممون پاس دادیم اینجا هم در اختیار ما قرار میگیره.

بریم یک مثال هم از این هوک جدیدی که در ری اکت 19 قراره داشته باشیمش ببینیم:

import { useFormStatus } from &quotreact-dom&quot

function Submit() {
  const status = useFormStatus();
  return <button disabled ? 'Submitting...' : 'Submit'}</button>;
}

const formAction = async () => {
  // Simulate a delay of 2 seconds
  await new Promise((resolve) => setTimeout(resolve, 3000));
}

const FormStatus = () => {
  return (
    <form action={formAction}>
      <Submit />
    </form>
  );
};

export default FormStatus;

هوک useFormState

یک هوک دیگه که قراره در ری اکت 19 داشته باشیم، useFormState هست که به ما اجازه میده یک state را طبق ثبت شدن یک فرم آپدیت کنیم.

const [state, formAction] = useFormState(fn, initialState, permalink?);
  • fn: یک فانکشن برای زمانیکه فرم ثبت بشه.
  • initialState: مقدار اولیه state ما
  • permalink: یک آپشن اختیاری که میتونیم بوسیلش تعیین کنیم درصورت اجرا شدن fn در سمت سرور، کاربر را به چه صفحه ای ریدایرکت کنه.
  • state: مقدار state ما هست و در اول کار هم که مساوی با initialState خواهد بود.
  • formAction: یک action که به اتریبیوت action فرم ما پاس داده میشه و مقداری که return میکنه هم در state در دسترس خواهد بود.

یک مثال هم از نحوه کار این هوک:

import { useFormState} from 'react-dom';

const FormState = () => {
    const submitForm = (prevState, queryData) => {
        const name =  queryData.get(&quotusername&quot);
        console.log(prevState); // previous form state
        if(name === 'john'){
            return {
                success: true,
                text: &quotWelcome&quot
            }
        }
        else{
            return {
                success: false,
                text: &quotError&quot
            }
        }
    }
    const [ message, formAction ] = useFormState(submitForm, null)
    return <form action={formAction}>
        <label>Name</label>
        <input type=&quottext&quot name=&quotusername&quot />
        <button>Submit</button>
        {message && <h1>{message.text}</h1>}
    </form>
}

export default FormState;

هوک useOptimistic

بوسیله این هوک میتونیم قبل از رسیدن جوابی برای عملیات های async پیغام یا یک state را به کاربر نشان بدیم. این هوک باعث میشه تا تجربه کاربری کمی بهتر بشه و سرعت نمایش پاسخ عملیات های async مثل http requests را بیشتر ( یجورایی لحظه ای میکنه )

const [ optimisticMessage, addOptimisticMessage] = useOptimistic(state, updatefn)

روشی به نام آپدیت خوشبینانه ( optimistic update ) داریم که در این روش ما قبل از تکمیل شدن مثلا یک درخواست http post میاییم نتیجه را در لحظه به کاربر نشان میدیم. به عنوان مثال وقتی کاربر کامنت جدیدی رو ثبت میکنه ما منتظر جواب بک اند نمیمونیم و فرض را بر موفقیت آمیز بودن ثبت کامنتش میزاریم ( رو همین حساب بهش میگیم آپدیت خوشبینانه ) و در لحظه کامنتی که ثبت کرده را در لیست کامنت ها نمایش میدیم و در پشت صحنه لیست کامنت هارو بروز میکنیم تا مطمعن هم بشیم که کامنت کاربر بصورت موفقیت آمیز ثبت شده باشه.

بریم یک مثال هم از این هوک ببینیم:

import { useOptimistic, useState, useRef } from &quotreact&quot

async function deliverMessage(message) {
  await new Promise((res) => setTimeout(res, 1000));
  return message;
}

function Thread({ messages, sendMessage }) {
  const formRef = useRef();
  async function formAction(formData) {
    addOptimisticMessage(formData.get(&quotmessage&quot));
    formRef.current.reset();
    await sendMessage(formData);
  }
  const [optimisticMessages, addOptimisticMessage] = useOptimistic(
    messages,
    (state, newMessage) => [
      ...state,
      {
        text: newMessage,
        sending: true,
      },
    ]
  );

  return (
    <>
      {optimisticMessages.map((message, index) => (
        <div key={index}>
          {message.text}
          {!!message.sending && <small> (Sending...)</small>}
        </div>
      ))}
      <form action={formAction} ref={formRef}>
        <input type=&quottext&quot name=&quotmessage&quot placeholder=&quotHello!&quot />
        <button type=&quotsubmit&quot>Send</button>
      </form>
    </>
  );
}

export default function App() {
  const [messages, setMessages] = useState([
    { text: &quotHello there!&quot, sending: false, key: 1 },
  ]);
  async function sendMessage(formData) {
    const sentMessage = await deliverMessage(formData.get(&quotmessage&quot));
    setMessages((messages) => [...messages, { text: sentMessage }]);
  }
  return <Thread messages={messages} sendMessage={sendMessage} />;
}

این مثال در داکیومنت خود ری اکت زده شده و میتونید خروجی این مثال را هم در این لینک ببینید.

آیا میشه الان از ری اکت 19 استفاده کرد ؟

در حال حاضر تمام ویژگی های جدیدی که در این مقاله دربارشون خواندیم، در نسخه canary ری اکت وجود داره و ورژن 19 ری اکت هنوز بطور رسمی منتشر نشده. طبق توصیه داکیومنت ری اکت، بهتره که از نسخه های canary در پروژه های مهم استفاده نکنید و صرفا جهت یادگیری و گشت و گذار در دل ویژگی های جدید ری اکت از اونها استفاده کنید. همچنین این نسخه از ری اکت را قراره در دوره آموزش فرانت اند هم بطور کامل آموزش بدیم.

منبع: https://vaspar.io/blog/react-19-overview