React18

React 18 มีอะไรเพิ่มมาใหม่บ้าง ?

22 minutes read
React18
Share on facebook
Share on twitter
Share on telegram
Share on linkedin
Share on email
สรุปเนื้อหา React18 เวอร์ชั่นใหม่มีอะไรมาใหม่บ้าง พร้อมตัวอย่างฟีเจอร์แบบต่าง ๆ อธิบายแบบเข้าใจง่ายครับ

React 18 เวอร์ชั่นใหม่มาแล้วววว

หลังจากที่ปล่อย React 17 ไปเมื่อปลายเดือนต.ค. ปีที่แล้ว ตอนนี้ก็เป็นระยะเวลากว่า 8 เดือน ทาง React official ก็ได้ประกาศสิ่งที่จะเพิ่มขึ้นใน React 18 นี้ ซึ่งแอดต้องบอกว่าเป็นสิ่งที่น่าสนใจมาก วันนี้แอดเลยจะมาเหลาให้ฟังว่า มีอะไร update ไปบ้างรวมทั้งวิธีการใช้งาน React 18 alpha(ทดลอง) อีกด้วย พวกเราไปเริ่มกันดีกว่า

React18
4 สิ่งที่เพิ่มเข้ามาใน React18

The new Root API

React18 New Root API
1. New Root API

ปกติเวลาที่เราจะทำการ render App ไปที่ root บน web page เรามักจะเขียน code ตามข้างล่าง ซึ่งเราจะเรียกวิธีนี้ว่า Legacy Root API

ReactDOM.render(<App/>, document.getElementById('root'))

แต่ว่าใน React 18 เราจะเขียนต่างออกไป ด้วยการที่เราจะสร้างตัวแปร root ขึ้นมาเพื่อใช้กับ method ใหม่ที่มีนามว่า “createRoot” ซึ่งมันจะทำการ pass root element และหลังจากนั้น เราจะทำการ root.render เพื่อ pass App component ตามมาเป็นลำดับขั้น

const root = ReactDOM.createRoot(document.getElementById('root'))
root.render(<App/>)

เพื่อนๆจะสังเกตุได้ว่า ผลลัพธ์ของทั้งสองวิธีก็ไม่เห็นต่างกันนี่ !!! ใช่ครับ มันไม่ต่าง แต่ขั้นตอนการเขียนมันต่างกัน วิธีการเขียนแบบใหม่จะทำให้เราสามารถใช้สิ่งใหม่ๆใน React 18 ได้ เช่น Concurrent feature, Automic batching ซึ่งเป็นการเริ่มต้นใช้งาน React 18 ให้สมบูรณ์แค่นั้นเอง เรายังสามารถเขียนแบบ Legacy Root API ได้ใน React 18 เช่นเดิมแต่เราไม่สามารถใช้บางอย่างที่เพิ่มเข้ามาได้ใน React 18 เช่นกัน

React 18: Concurrent Features

React18 3 1
2. Concurrent Feature

1. useTransition

const SUSPENSE_CONFIG = { timeoutMs: 2000 };

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

การใช้ useTransition จะทำให้ component ไม่ต้องเข้า loading state ที่ไม่จำเป็น ด้วยการรอ content ที่จะโหลดก่อนการเคลื่อนย้ายไปหน้าถัดไป component สามารถที่จะเลื่อนระยะเวลาในส่วน data fetching ได้จนกว่าจะมี render ภายหลังเพื่อให้สามารถแสดงการ update ที่สำคัญกว่าในทันที

การใช้ useTransition hook จะ return 2 ค่า ใน array ได้แก่

1. startTransition เป็น function ที่ใช้ในการทำ callback เราสามารถใช้เพื่อบอก React ว่า state ไหนที่ต้องการเลื่อนระยะเวลา ซึ่งช่วยในการรักษา UI responsive รวมทั้งการ interaction กับ website ด้วย

StartTransition(() => {
  setText(input);
});

2. isPending เป็น boolean function ที่ใช้ในการตรวจสอบว่า สิ่งที่เรารออยู่ เกิดการเปลี่ยนแปลงเสร็จสิ้นหรือยัง

2. useDeferredValue

const deferredValue = useDeferredValue(value, { timeoutMS: 2000 });

ปกติเวลาที่เราทำการ click หรือ hover บางครั้ง ตัว website อาจเกิด action บางสิ่งขึ้นมาทันทีซึ่งมันส่งผลต่อ UI responsive อย่างมาก การใช้ useDeferredValue จะเป็นเหมือนการหน่วงเวลาการ action ที่เกิดขึ้นทำให้ UI responsive ไม่ได้รับผลกระทบที่เกิดขึ้น

ยกตัวอย่างเช่น Text input

function App() {
  const [text, setText] = useState("hello");
  const deferredText = useDeferredValue(text, { timeoutMs: 2000 }); 

  return (
    <div className="App">
      {/* Keep passing the current text to the input */}
      <input value={text} onChange={handleChange} />
      ...
      {/* But the list is allowed to "lag behind" when necessary */}
      <MySlowList text={deferredText} />
    </div>
  );
 }

ผลลัพธ์ของ code นี้ จะทำให้เราสามารถใส่ input ที่เป็น text และแสดงผล input ที่ใส่เข้าไปทันที ซึ่งให้ความรู้สึก responsive ยังไงยังงั้น เราได้ตั้งค่า MyslowList มี delay 2 วินาทีตาม timeoutMs ก่อนการ update ซึ่งจะทำให้สามารถ render ด้วย text ณ ปัจจุบันได้ทันทีนั่นดอง

Suspense API

React18 Suspense API
3. Suspense API

1. Suspense

<Suspense fallback={<h1>Loading...</h1>}>
  <ProfilePhoto />
  <ProfileDetails />
</Suspense>

การใช้ Suspense component จะทำให้ component ที่อยู่ใน Suspense สามารถ “รอ” ได้ก่อนที่จะ render โดยสามารถแสดง text ใน fallback ขณะที่รออยู่ ยกตัวอย่างข้างบน ProfileDetails กำลังรอ async API เรียกเพื่อไป fetch ข้อมูลบางอย่าง ขณะที่รอ ProfileDetails และ ProfilePhoto จะแสดงข้อความขึ้นมาว่า “Loading…”

Suspense มีคุณสมบัติ(Properties) 2 แบบ

  1. fallback คือเป็นตัวบ่งชี้ว่าตอนนี้กำลังรออยู่ ซึ่ง fallback จะแสดงผลลัพธ์ที่ตั้งไว้ออกมาจนกระทั่ง children ใน Suspense componet ทุกตัว render เสร็จสมบูรณ์แล้ว
  2. unstable_avoidThisFallback เป็นประเภท boolean ซึ่งจะบอก React ว่าต้อง “ข้าม” ขอบเขตการโหลดช่วงแรกหรือไม่ ในอนาคตคุณสมบัตินี้จะถูกลบออกไป

2. SuspenseList

<SuspenseList revealOrder="forwards">
  <Suspense fallback={'Loading...'}>
    <ProfilePicture id={1} />
  </Suspense>
  <Suspense fallback={'Loading...'}>
    <ProfilePicture id={2} />
  </Suspense>
  <Suspense fallback={'Loading...'}>
    <ProfilePicture id={3} />
  </Suspense>
  ...
</SuspenseList>

SuspenseList จะช่วยในการประสานงานและจัดลำดับกับ component หลายตัวที่อยู่ใน Suspense เพื่อที่จะแสดงผลแก่ user ให้มีลำดับขั้นมากขึ้น ตัวอย่างเช่น เมื่อ component หลายตัวต้องการที่จะ fetch ข้อมูล ข้อมูลที่ได้จะมาแบบมั่วไปหมด แต่ถ้าเราใช้ SuspenseList, React จะไม่แสดง item ใน list ก่อน จน item ก่อนหน้านี้แสดงผลเรียบร้อยแล้ว นั่นทำให้การแสดงผล Fallback มีลำดับขั้นตอนนั่นเอง

SuspenseList มีคุณสมบัติ(Properties) 2 แบบ

  1. revealOrder (forwards, backwards, together) จะเป็นตัวกำหนดลำดับที่ children ของ SuspenseList จะแสดงผล โดย together หมายความว่า แสดงผลทุกตัวเมื่อพร้อมเลยแทนที่จะแสดงผลแค่ตัวเดียว
  2. tail(collapsed, hidden) จะเป็นตัวกำหนดการแสดงผล item ที่ยังไม่ได้โหลดใน SuspenseList โดยค่าเริ่มต้น จะแสดงผล fallback ทุกตัวใน list , collapsed แสดงผลแค่ fallback ถัดไป ใน list ส่วน hidden จะไม่แสดงผล

Automatic batching

React18 Automatic batching
4. Automatic batching

Batching คือการรวบกลุ่ม state ที่เกิดการ update พร้อมกันให้ render เพียงครั้งเดียวเพื่อประสิทธิภาพที่ดีขึ้น

ยกตัวอย่างเช่น ถ้าเรามี 2 stateที่ต้องรอ update ด้วย click event เดียวกัน React จะรวมทั้ง 2 state ให้เป็น 1 re-render และถ้าเพื่อนๆลองรัน code ข้างล่างจะเห็นว่าในการ click ทุกครั้ง React ใช้แค่ render เดียวเสมอ

function App() {
  const [count, setCount] = useState(0);
  const [flag, setFlag] = useState(false);

  function handleClick() {
    setCount(c => c + 1); // Does not re-render yet
    setFlag(f => !f); // Does not re-render yet
    // React will only re-render once at the end (that's batching!)
  }

  return (
    <div>
      <button onClick={handleClick}>Next</button>
      <h1 style={{ color: flag ? "blue" : "black" }}>{count}</h1>
    </div>
  );
}

อย่างไรก็ตาม React ไม่ได้ทำอย่างนี้เสมอไป บางครั้งถ้าเราต้องการ fetch data และเกิด handleClick update ขึ้น React จะไม่รวมทั้ง 2 states และ update ทั้ง 2 state แยกกันแทน

นั่นเป็นเพราะว่า React แค่ทำการรวม update ที่เหมือนกันเท่านั้นแต่กับเกิดการ update state หลังจาก event ถูก handle ไปแล้ว(fetch callback) จะไม่ถูกรวม ปัญหานี้มีมาเรื่อยๆจนมาถึงปัจจุบัน ซึ่งนั่นทำให้ React 18 ได้มีแนวคิดเกี่ยวกับ batch ใหม่ มาจาก createRoot ใน New Root API ที่ว่า จะรวมทุก state ที่รอ update โดยไม่สนว่าจุดกำเนิดจะมาจากไหน นั่นหมายความว่า การ Update ใน timeouts, promises, native event handles หรือ event อะไรก็ตาม จะถูกรวมกลุ่มเหมือนกับการ Update ใน React event ซึ่งผลลัพธ์ที่ได้จะทำให้เกิดการ render น้อยลง และทำให้ performance ใน application ของคุณดีขึ้นอีกด้วย

function App() {
  const [count, setCount] = useState(0);
  const [flag, setFlag] = useState(false);

  function handleClick() {
    fetchSomething().then(() => {
      // React 18 and later DOES batch these:
      setCount(c => c + 1);
      setFlag(f => !f);
      // React will only re-render once at the end (that's batching!)
    });
  }

  return (
    <div>
      <button onClick={handleClick}>Next</button>
      <h1 style={{ color: flag ? "blue" : "black" }}>{count}</h1>
    </div>
  );
}

React จะรวมกลุ่ม update อัตโนมัติ โดยไม่สนว่าเกิดการ Update ที่จุดไหน อย่างเช่น:

function handleClick() {
  setCount(c => c + 1);
  setFlag(f => !f);
  // React will only re-render once at the end (that's batching!)
}

เช่นเดียวกับ

setTimeout(() => {
  setCount(c => c + 1);
  setFlag(f => !f);
  // React will only re-render once at the end (that's batching!)
}, 1000);

เช่นเดียวกับ

fetch(/*...*/).then(() => {
  setCount(c => c + 1);
  setFlag(f => !f);
  // React will only re-render once at the end (that's batching!)
})

เช่นเดียวกับ

elm.addEventListener('click', () => {
  setCount(c => c + 1);
  setFlag(f => !f);
  // React will only re-render once at the end (that's batching!)
});

สรุป

ตอนนี้ React 18 ยังอยู่ในช่วง alpha test ในวันที่ปล่อยตัวสมบูรณ์ อาจมีการเปลี่ยนแปลงมากมายนอกเหนือจากที่พูดไป แก่ feature ใหม่ๆขึ้นได้ในอนาคต React 18 มีข่าวว่าจะมีการปล่อยตัวเต็มในไม่กี่เดือนข้างหน้า ถ้าเพื่อนๆสนใจอยากลองโหลดมาใช้ดูก่อนสามารถดูวิธีลงได้ที่ Everything New In React 18! วันนี้ขอจบการรายงาน React 18 เพียงเท่านี้ ขอให้ทุกคนสนุกกับการเขียนโค้ด!!! May the fun be with your code!!!

ขอบคุณเนื้อหาอ้างอิงจากเว็บไซต์

บทความที่เกี่ยวข้อง

Share on facebook
Share on twitter
Share on telegram
Share on linkedin
Share on email