ไทย

เรียนรู้วิธีที่ Automatic Batching ของ React ช่วยเพิ่มประสิทธิภาพการอัปเดต State หลายรายการ ปรับปรุงประสิทธิภาพของแอปพลิเคชันและป้องกันการ re-render ที่ไม่จำเป็น พร้อมสำรวจตัวอย่างและแนวทางปฏิบัติที่ดีที่สุด

React Automatic Batching: การเพิ่มประสิทธิภาพการอัปเดต State เพื่อประสิทธิภาพสูงสุด

ประสิทธิภาพของ React เป็นสิ่งสำคัญอย่างยิ่งในการสร้าง User Interface ที่ราบรื่นและตอบสนองได้ดี หนึ่งในคุณสมบัติหลักที่ถูกนำมาใช้เพื่อปรับปรุงประสิทธิภาพคือ automatic batching เทคนิคการเพิ่มประสิทธิภาพนี้จะจัดกลุ่มการอัปเดต State หลายรายการให้เป็นการ re-render เพียงครั้งเดียวโดยอัตโนมัติ ซึ่งนำไปสู่การเพิ่มประสิทธิภาพอย่างมีนัยสำคัญ โดยเฉพาะอย่างยิ่งในแอปพลิเคชันที่ซับซ้อนและมีการเปลี่ยนแปลง State บ่อยครั้ง

React Automatic Batching คืออะไร?

Batching ในบริบทของ React คือกระบวนการจัดกลุ่มการอัปเดต State หลายรายการให้เป็นการอัปเดตเพียงครั้งเดียว ก่อนหน้า React 18, batching จะถูกใช้กับการอัปเดตที่เกิดขึ้นภายใน React event handler เท่านั้น การอัปเดตที่อยู่นอก event handler เช่น ภายใน setTimeout, promises หรือ native event handlers จะไม่ถูกจัดกลุ่ม ซึ่งอาจนำไปสู่การ re-render ที่ไม่จำเป็นและปัญหาคอขวดด้านประสิทธิภาพ

React 18 ได้เปิดตัว automatic batching ซึ่งขยายการเพิ่มประสิทธิภาพนี้ไปยังการอัปเดต State ทั้งหมด ไม่ว่าจะเกิดขึ้นที่ใดก็ตาม ซึ่งหมายความว่าไม่ว่าการอัปเดต State ของคุณจะเกิดขึ้นภายใน React event handler, callback ของ setTimeout หรือการ resolve ของ promise, React จะจัดกลุ่มการอัปเดตเหล่านั้นเข้าด้วยกันเป็นการ re-render เพียงครั้งเดียวโดยอัตโนมัติ

ทำไม Automatic Batching จึงมีความสำคัญ?

Automatic batching มีประโยชน์หลักหลายประการ:

Automatic Batching ทำงานอย่างไร

React ทำ automatic batching ได้โดยการหน่วงเวลาการดำเนินการอัปเดต State ไปจนถึงจุดสิ้นสุดของ execution context ปัจจุบัน ซึ่งช่วยให้ React สามารถรวบรวมการอัปเดต State ทั้งหมดที่เกิดขึ้นในช่วง context นั้นและจัดกลุ่มเข้าด้วยกันเป็นการอัปเดตเพียงครั้งเดียว

พิจารณาตัวอย่างง่ายๆ นี้:

function ExampleComponent() {
  const [count1, setCount1] = useState(0);
  const [count2, setCount2] = useState(0);

  function handleClick() {
    setTimeout(() => {
      setCount1(count1 + 1);
      setCount2(count2 + 1);
    }, 0);
  }

  return (
    <div>
      <p>Count 1: {count1}</p>
      <p>Count 2: {count2}</p>
      <button onClick={handleClick}>Increment</button>
    </div>
  );
}

ก่อน React 18 การคลิกปุ่มจะทำให้เกิดการ re-render สองครั้ง: ครั้งแรกสำหรับ setCount1 และอีกครั้งสำหรับ setCount2 แต่ด้วย automatic batching ใน React 18 การอัปเดต State ทั้งสองจะถูกจัดกลุ่มเข้าด้วยกัน ส่งผลให้เกิดการ re-render เพียงครั้งเดียว

ตัวอย่างการทำงานของ Automatic Batching

1. การอัปเดตแบบอะซิงโครนัส (Asynchronous Updates)

การดำเนินการแบบอะซิงโครนัส เช่น การดึงข้อมูลจาก API มักจะมีการอัปเดต State หลังจากที่การดำเนินการเสร็จสิ้น Automatic batching ช่วยให้แน่ใจว่าการอัปเดต State เหล่านี้ถูกจัดกลุ่มเข้าด้วยกัน แม้ว่าจะเกิดขึ้นภายใน callback แบบอะซิงโครนัสก็ตาม

function DataFetchingComponent() {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    async function fetchData() {
      try {
        const response = await fetch('https://api.example.com/data');
        const jsonData = await response.json();
        setData(jsonData);
        setLoading(false);
      } catch (error) {
        console.error('Error fetching data:', error);
        setLoading(false);
      }
    }

    fetchData();
  }, []);

  if (loading) {
    return <p>Loading...</p>;
  }

  return <div>Data: {JSON.stringify(data)}</div>;
}

ในตัวอย่างนี้ setData และ setLoading ถูกเรียกใช้ภายในฟังก์ชัน fetchData แบบอะซิงโครนัส React จะจัดกลุ่มการอัปเดตเหล่านี้เข้าด้วยกัน ส่งผลให้มีการ re-render เพียงครั้งเดียวเมื่อข้อมูลถูกดึงมาและสถานะการโหลดถูกอัปเดตแล้ว

2. Promises

เช่นเดียวกับการอัปเดตแบบอะซิงโครนัส promises มักจะมีการอัปเดต State เมื่อ promise สำเร็จ (resolve) หรือล้มเหลว (reject) Automatic batching ช่วยให้แน่ใจว่าการอัปเดต State เหล่านี้จะถูกจัดกลุ่มเข้าด้วยกันเช่นกัน

function PromiseComponent() {
  const [result, setResult] = useState(null);
  const [error, setError] = useState(null);

  useEffect(() => {
    const myPromise = new Promise((resolve, reject) => {
      setTimeout(() => {
        const success = Math.random() > 0.5;
        if (success) {
          resolve('Promise resolved!');
        } else {
          reject('Promise rejected!');
        }
      }, 1000);
    });

    myPromise
      .then((value) => {
        setResult(value);
        setError(null);
      })
      .catch((err) => {
        setError(err);
        setResult(null);
      });
  }, []);

  if (error) {
    return <p>Error: {error}</p>;
  }

  if (result) {
    return <p>Result: {result}</p>;
  }

  return <p>Loading...</p>;
}

ในกรณีนี้ ไม่ว่าจะเป็นการเรียก setResult และ setError(null) เมื่อสำเร็จ หรือการเรียก setError และ setResult(null) เมื่อล้มเหลว Automatic batching จะรวมการอัปเดตเหล่านี้ให้เป็นการ re-render เพียงครั้งเดียว

3. Native Event Handlers

บางครั้ง คุณอาจต้องใช้ native event handlers (เช่น addEventListener) แทนที่จะเป็น synthetic event handlers ของ React ซึ่ง automatic batching ก็ยังคงทำงานในกรณีเหล่านี้ด้วย

function NativeEventHandlerComponent() {
  const [scrollPosition, setScrollPosition] = useState(0);

  useEffect(() => {
    function handleScroll() {
      setScrollPosition(window.scrollY);
    }

    window.addEventListener('scroll', handleScroll);

    return () => {
      window.removeEventListener('scroll', handleScroll);
    };
  }, []);

  return <p>Scroll Position: {scrollPosition}</p>;
}

แม้ว่า setScrollPosition จะถูกเรียกใช้ภายใน native event handler, React ก็ยังคงจัดกลุ่มการอัปเดตเข้าด้วยกัน เพื่อป้องกันการ re-render ที่มากเกินไปในขณะที่ผู้ใช้เลื่อนหน้าจอ

การเลือกไม่ใช้ Automatic Batching

ในบางกรณีที่เกิดขึ้นไม่บ่อยนัก คุณอาจต้องการเลือกไม่ใช้ automatic batching ตัวอย่างเช่น คุณอาจต้องการบังคับให้มีการอัปเดตแบบซิงโครนัสเพื่อให้แน่ใจว่า UI ได้รับการอัปเดตทันที React ได้เตรียม API ชื่อว่า flushSync ไว้สำหรับจุดประสงค์นี้

หมายเหตุ: การใช้ flushSync ควรทำเท่าที่จำเป็นเท่านั้น เพราะอาจส่งผลเสียต่อประสิทธิภาพได้ โดยทั่วไปแล้วควรพึ่งพา automatic batching ทุกครั้งที่ทำได้

import { flushSync } from 'react-dom';

function ExampleComponent() {
  const [count, setCount] = useState(0);

  function handleClick() {
    flushSync(() => {
      setCount(count + 1);
    });
  }

  return (<button onClick={handleClick}>Increment</button>);
}

ในตัวอย่างนี้ flushSync จะบังคับให้ React อัปเดต State และ re-render component ทันที โดยข้ามกระบวนการ automatic batching

แนวทางปฏิบัติที่ดีที่สุดสำหรับการเพิ่มประสิทธิภาพการอัปเดต State

แม้ว่า automatic batching จะช่วยปรับปรุงประสิทธิภาพได้อย่างมาก แต่ก็ยังคงเป็นสิ่งสำคัญที่จะต้องปฏิบัติตามแนวทางที่ดีที่สุดสำหรับการเพิ่มประสิทธิภาพการอัปเดต State:

Automatic Batching และข้อควรพิจารณาในระดับสากล

Automatic batching ซึ่งเป็นการเพิ่มประสิทธิภาพหลักของ React ให้ประโยชน์กับแอปพลิเคชันทั่วโลกโดยไม่คำนึงถึงตำแหน่งของผู้ใช้ ความเร็วของเครือข่าย หรืออุปกรณ์ อย่างไรก็ตาม ผลกระทบของมันจะเห็นได้ชัดเจนยิ่งขึ้นในสถานการณ์ที่มีการเชื่อมต่ออินเทอร์เน็ตที่ช้าหรืออุปกรณ์ที่มีประสิทธิภาพต่ำ สำหรับผู้ชมในระดับนานาชาติ ควรพิจารณาประเด็นเหล่านี้:

สรุป

React automatic batching เป็นเทคนิคการเพิ่มประสิทธิภาพที่ทรงพลังซึ่งสามารถปรับปรุงประสิทธิภาพของแอปพลิเคชัน React ของคุณได้อย่างมาก ด้วยการจัดกลุ่มการอัปเดต State หลายรายการให้เป็นการ re-render เพียงครั้งเดียวโดยอัตโนมัติ ช่วยลดภาระในการเรนเดอร์ ป้องกันสถานะที่ไม่สอดคล้องกัน และนำไปสู่ประสบการณ์ผู้ใช้ที่ราบรื่นและตอบสนองได้ดียิ่งขึ้น การทำความเข้าใจวิธีการทำงานของ automatic batching และปฏิบัติตามแนวทางที่ดีที่สุดสำหรับการเพิ่มประสิทธิภาพการอัปเดต State จะช่วยให้คุณสามารถสร้างแอปพลิเคชัน React ที่มีประสิทธิภาพสูงซึ่งมอบประสบการณ์ที่ยอดเยี่ยมแก่ผู้ใช้ทั่วโลก การใช้เครื่องมืออย่าง React DevTools จะช่วยปรับปรุงและเพิ่มประสิทธิภาพโปรไฟล์ของแอปพลิเคชันของคุณในสภาพแวดล้อมที่หลากหลายทั่วโลกได้ดียิ่งขึ้น