ไทย

คู่มือฉบับสมบูรณ์เพื่อเพิ่มประสิทธิภาพแอปพลิเคชัน React ด้วย useMemo, useCallback และ React.memo เรียนรู้วิธีป้องกันการ re-render ที่ไม่จำเป็นและปรับปรุงประสบการณ์ผู้ใช้

การเพิ่มประสิทธิภาพ React: เชี่ยวชาญ useMemo, useCallback, และ React.memo

React ซึ่งเป็นไลบรารี JavaScript ยอดนิยมสำหรับสร้างส่วนติดต่อผู้ใช้ (user interface) เป็นที่รู้จักกันดีในเรื่องสถาปัตยกรรมแบบคอมโพเนนต์และสไตล์การเขียนโค้ดแบบประกาศ (declarative style) อย่างไรก็ตาม เมื่อแอปพลิเคชันมีความซับซ้อนมากขึ้น ประสิทธิภาพอาจกลายเป็นปัญหา การ re-render คอมโพเนนต์ที่ไม่จำเป็นอาจนำไปสู่ประสิทธิภาพที่เชื่องช้าและประสบการณ์ผู้ใช้ที่ไม่ดี โชคดีที่ React มีเครื่องมือหลายอย่างเพื่อเพิ่มประสิทธิภาพ รวมถึง useMemo, useCallback, และ React.memo คู่มือนี้จะเจาะลึกเทคนิคเหล่านี้ พร้อมตัวอย่างที่นำไปใช้ได้จริงและข้อมูลเชิงลึกที่นำไปปฏิบัติได้ เพื่อช่วยให้คุณสร้างแอปพลิเคชัน React ที่มีประสิทธิภาพสูง

ทำความเข้าใจการ Re-render ของ React

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

ทำไม Component ถึง Re-render

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

แนะนำ useMemo: การจำค่าการคำนวณที่สิ้นเปลือง

useMemo เป็น React hook ที่ทำการจำ (memoize) ผลลัพธ์ของการคำนวณ การจำค่า (Memoization) เป็นเทคนิคการเพิ่มประสิทธิภาพที่เก็บผลลัพธ์ของการเรียกใช้ฟังก์ชันที่สิ้นเปลือง และนำผลลัพธ์เหล่านั้นกลับมาใช้ใหม่เมื่อมีการป้อนข้อมูลเดิมอีกครั้ง ซึ่งช่วยป้องกันความจำเป็นในการรันฟังก์ชันซ้ำโดยไม่จำเป็น

ควรใช้ useMemo เมื่อใด

useMemo ทำงานอย่างไร

useMemo รับอาร์กิวเมนต์สองตัว:

  1. ฟังก์ชันที่ทำการคำนวณ
  2. อาร์เรย์ของ dependencies (ค่าที่ฟังก์ชันต้องพึ่งพา)

ฟังก์ชันจะถูกเรียกใช้ก็ต่อเมื่อหนึ่งใน dependencies ในอาร์เรย์มีการเปลี่ยนแปลงเท่านั้น มิฉะนั้น useMemo จะส่งคืนค่าที่ถูกจำไว้ก่อนหน้านี้

ตัวอย่าง: การคำนวณลำดับฟีโบนัชชี

ลำดับฟีโบนัชชีเป็นตัวอย่างคลาสสิกของการคำนวณที่ใช้พลังประมวลผลสูง มาสร้างคอมโพเนนต์ที่คำนวณเลขฟีโบนัชชีตัวที่ n โดยใช้ useMemo กัน


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

function Fibonacci({ n }) {
  const fibonacciNumber = useMemo(() => {
    console.log('Calculating Fibonacci...'); // แสดงให้เห็นว่าการคำนวณทำงานเมื่อใด
    function calculateFibonacci(num) {
      if (num <= 1) {
        return num;
      }
      return calculateFibonacci(num - 1) + calculateFibonacci(num - 2);
    }
    return calculateFibonacci(n);
  }, [n]);

  return 

Fibonacci({n}) = {fibonacciNumber}

; } function App() { const [number, setNumber] = useState(5); return (
setNumber(parseInt(e.target.value))} />
); } export default App;

ในตัวอย่างนี้ ฟังก์ชัน calculateFibonacci จะถูกเรียกใช้ก็ต่อเมื่อ prop n เปลี่ยนแปลงเท่านั้น หากไม่มี useMemo ฟังก์ชันนี้จะถูกเรียกใช้ทุกครั้งที่มีการ re-render ของคอมโพเนนต์ Fibonacci แม้ว่า n จะยังคงเดิมก็ตาม ลองจินตนาการว่าการคำนวณนี้เกิดขึ้นบนแดชบอร์ดทางการเงินระดับโลก ทุกครั้งที่ตลาดขยับจะทำให้เกิดการคำนวณใหม่ทั้งหมด ซึ่งนำไปสู่ความล่าช้าอย่างมาก useMemo ช่วยป้องกันปัญหานั้นได้

แนะนำ useCallback: การจำฟังก์ชัน

useCallback เป็นอีกหนึ่ง React hook ที่ทำการจำฟังก์ชัน ซึ่งจะป้องกันการสร้างอินสแตนซ์ฟังก์ชันใหม่ในทุกๆ การ render ซึ่งมีประโยชน์อย่างยิ่งเมื่อส่งฟังก์ชัน callback เป็น props ไปยังคอมโพเนนต์ลูก

ควรใช้ useCallback เมื่อใด

useCallback ทำงานอย่างไร

useCallback รับอาร์กิวเมนต์สองตัว:

  1. ฟังก์ชันที่ต้องการจำ
  2. อาร์เรย์ของ dependencies

ฟังก์ชันจะถูกสร้างขึ้นใหม่ก็ต่อเมื่อหนึ่งใน dependencies ในอาร์เรย์มีการเปลี่ยนแปลงเท่านั้น มิฉะนั้น useCallback จะส่งคืนอินสแตนซ์ของฟังก์ชันเดิม

ตัวอย่าง: การจัดการการคลิกปุ่ม

มาสร้างคอมโพเนนต์ที่มีปุ่มที่ทริกเกอร์ฟังก์ชัน callback กัน เราจะใช้ useCallback เพื่อจำฟังก์ชัน callback นั้น


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

function Button({ onClick, children }) {
  console.log('Button re-rendered'); // แสดงให้เห็นว่า Button re-render เมื่อใด
  return ;
}

const MemoizedButton = React.memo(Button);

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

  const handleClick = useCallback(() => {
    console.log('Button clicked');
    setCount((prevCount) => prevCount + 1);
  }, []); // dependency array ที่ว่างเปล่าหมายความว่าฟังก์ชันถูกสร้างขึ้นเพียงครั้งเดียว

  return (
    

Count: {count}

Increment
); } export default App;

ในตัวอย่างนี้ ฟังก์ชัน handleClick ถูกสร้างขึ้นเพียงครั้งเดียวเนื่องจาก dependency array ว่างเปล่า เมื่อคอมโพเนนต์ App re-render เนื่องจากการเปลี่ยนแปลงของ state count ฟังก์ชัน handleClick จะยังคงเป็นตัวเดิม คอมโพเนนต์ MemoizedButton ที่ถูกห่อด้วย React.memo จะ re-render ก็ต่อเมื่อ props ของมันเปลี่ยนแปลงเท่านั้น เนื่องจาก prop onClick (คือ handleClick) ยังคงเหมือนเดิม คอมโพเนนต์ Button จึงไม่ re-render โดยไม่จำเป็น ลองนึกถึงแอปพลิเคชันแผนที่แบบอินเทอร์แอคทีฟ ทุกครั้งที่ผู้ใช้โต้ตอบ อาจมีคอมโพเนนต์ปุ่มหลายสิบตัวที่ได้รับผลกระทบ หากไม่มี useCallback ปุ่มเหล่านี้จะ re-render โดยไม่จำเป็น ทำให้ประสบการณ์การใช้งานกระตุก การใช้ useCallback จะช่วยให้การโต้ตอบราบรื่นขึ้น

แนะนำ React.memo: การจำ Component

React.memo เป็น higher-order component (HOC) ที่ทำการจำ functional component มันจะป้องกันไม่ให้คอมโพเนนต์ re-render หาก props ของมันไม่มีการเปลี่ยนแปลง ซึ่งคล้ายกับ PureComponent สำหรับ class components

ควรใช้ React.memo เมื่อใด

React.memo ทำงานอย่างไร

React.memo จะห่อหุ้ม functional component และทำการเปรียบเทียบ props ก่อนหน้าและ props ปัจจุบันแบบตื้นๆ (shallow comparison) หาก props เหมือนกัน คอมโพเนนต์จะไม่ re-render

ตัวอย่าง: การแสดงโปรไฟล์ผู้ใช้

มาสร้างคอมโพเนนต์ที่แสดงโปรไฟล์ผู้ใช้กัน เราจะใช้ React.memo เพื่อป้องกันการ re-render ที่ไม่จำเป็นหากข้อมูลของผู้ใช้ไม่มีการเปลี่ยนแปลง


import React from 'react';

function UserProfile({ user }) {
  console.log('UserProfile re-rendered'); // แสดงให้เห็นว่า component re-render เมื่อใด
  return (
    

Name: {user.name}

Email: {user.email}

); } const MemoizedUserProfile = React.memo(UserProfile, (prevProps, nextProps) => { // ฟังก์ชันเปรียบเทียบแบบกำหนดเอง (optional) return prevProps.user.id === nextProps.user.id; // re-render ต่อเมื่อ ID ของผู้ใช้เปลี่ยนแปลง }); function App() { const [user, setUser] = React.useState({ id: 1, name: 'John Doe', email: 'john.doe@example.com', }); const updateUser = () => { setUser({ ...user, name: 'Jane Doe' }); // เปลี่ยนชื่อ }; return (
); } export default App;

ในตัวอย่างนี้ คอมโพเนนต์ MemoizedUserProfile จะ re-render ก็ต่อเมื่อ prop user.id เปลี่ยนแปลงเท่านั้น แม้ว่าคุณสมบัติอื่นๆ ของอ็อบเจกต์ user จะเปลี่ยนแปลง (เช่น ชื่อหรืออีเมล) คอมโพเนนต์ก็จะไม่ re-render เว้นแต่ ID จะแตกต่างกัน ฟังก์ชันเปรียบเทียบที่กำหนดเองนี้ภายใน `React.memo` ช่วยให้สามารถควบคุมการ re-render ของคอมโพเนนต์ได้อย่างละเอียด ลองนึกถึงแพลตฟอร์มโซเชียลมีเดียที่มีการอัปเดตโปรไฟล์ผู้ใช้อยู่ตลอดเวลา หากไม่มี `React.memo` การเปลี่ยนสถานะหรือรูปโปรไฟล์ของผู้ใช้อาจทำให้คอมโพเนนต์โปรไฟล์ทั้งหมด re-render แม้ว่ารายละเอียดหลักของผู้ใช้จะยังคงเหมือนเดิม `React.memo` ช่วยให้สามารถอัปเดตแบบเฉพาะเจาะจงและปรับปรุงประสิทธิภาพได้อย่างมาก

การใช้ useMemo, useCallback, และ React.memo ร่วมกัน

เทคนิคทั้งสามนี้จะมีประสิทธิภาพสูงสุดเมื่อใช้ร่วมกัน useMemo ใช้จำการคำนวณที่สิ้นเปลือง useCallback ใช้จำฟังก์ชัน และ React.memo ใช้จำคอมโพเนนต์ การผสมผสานเทคนิคเหล่านี้จะช่วยให้คุณลดจำนวนการ re-render ที่ไม่จำเป็นในแอปพลิเคชัน React ของคุณได้อย่างมาก

ตัวอย่าง: Component ที่ซับซ้อน

มาสร้างคอมโพเนนต์ที่ซับซ้อนขึ้นเพื่อสาธิตวิธีการรวมเทคนิคเหล่านี้เข้าด้วยกัน


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

function ListItem({ item, onUpdate, onDelete }) {
  console.log(`ListItem ${item.id} re-rendered`); // แสดงให้เห็นว่า component re-render เมื่อใด
  return (
    
  • {item.text}
  • ); } const MemoizedListItem = React.memo(ListItem); function List({ items, onUpdate, onDelete }) { console.log('List re-rendered'); // แสดงให้เห็นว่า component re-render เมื่อใด return (
      {items.map((item) => ( ))}
    ); } const MemoizedList = React.memo(List); function App() { const [items, setItems] = useState([ { id: 1, text: 'Item 1' }, { id: 2, text: 'Item 2' }, { id: 3, text: 'Item 3' }, ]); const handleUpdate = useCallback((id) => { setItems((prevItems) => prevItems.map((item) => item.id === id ? { ...item, text: `Updated ${item.text}` } : item ) ); }, []); const handleDelete = useCallback((id) => { setItems((prevItems) => prevItems.filter((item) => item.id !== id)); }, []); const memoizedItems = useMemo(() => items, [items]); return (
    ); } export default App;

    ในตัวอย่างนี้:

    การผสมผสานเทคนิคเหล่านี้ช่วยให้แน่ใจว่าคอมโพเนนต์จะ re-render เฉพาะเมื่อจำเป็นเท่านั้น ซึ่งนำไปสู่การปรับปรุงประสิทธิภาพอย่างมาก ลองจินตนาการถึงเครื่องมือจัดการโครงการขนาดใหญ่ที่มีรายการงานที่ถูกอัปเดต ลบ และจัดลำดับใหม่อยู่ตลอดเวลา หากไม่มีการปรับปรุงประสิทธิภาพเหล่านี้ การเปลี่ยนแปลงเล็กน้อยในรายการงานจะทริกเกอร์การ re-render เป็นทอดๆ ทำให้แอปพลิเคชันช้าและไม่ตอบสนอง การใช้ useMemo, useCallback, และ React.memo อย่างมีกลยุทธ์จะช่วยให้แอปพลิเคชันยังคงมีประสิทธิภาพแม้จะมีข้อมูลที่ซับซ้อนและการอัปเดตบ่อยครั้ง

    เทคนิคการเพิ่มประสิทธิภาพเพิ่มเติม

    แม้ว่า useMemo, useCallback, และ React.memo จะเป็นเครื่องมือที่ทรงพลัง แต่ก็ไม่ใช่ทางเลือกเดียวในการเพิ่มประสิทธิภาพ React นี่คือเทคนิคเพิ่มเติมบางส่วนที่ควรพิจารณา:

    ข้อควรพิจารณาในการเพิ่มประสิทธิภาพสำหรับผู้ใช้ทั่วโลก

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

    สรุป

    การเพิ่มประสิทธิภาพแอปพลิเคชัน React เป็นสิ่งสำคัญอย่างยิ่งในการมอบประสบการณ์ผู้ใช้ที่ราบรื่นและตอบสนองได้ดี ด้วยการเรียนรู้เทคนิคต่างๆ เช่น useMemo, useCallback, และ React.memo และการพิจารณากลยุทธ์การเพิ่มประสิทธิภาพระดับโลก คุณสามารถสร้างแอปพลิเคชัน React ที่มีประสิทธิภาพสูงซึ่งสามารถปรับขนาดเพื่อตอบสนองความต้องการของผู้ใช้ที่หลากหลายได้ อย่าลืมทำการโปรไฟล์แอปพลิเคชันของคุณเพื่อระบุจุดคอขวดของประสิทธิภาพและใช้เทคนิคการเพิ่มประสิทธิภาพเหล่านี้อย่างมีกลยุทธ์ อย่าเพิ่มประสิทธิภาพก่อนเวลาอันควร – มุ่งเน้นไปที่ส่วนที่คุณจะได้รับผลกระทบที่สำคัญที่สุด

    คู่มือนี้เป็นรากฐานที่มั่นคงสำหรับความเข้าใจและการนำการเพิ่มประสิทธิภาพ React ไปใช้ ในขณะที่คุณพัฒนาแอปพลิเคชัน React ต่อไป อย่าลืมให้ความสำคัญกับประสิทธิภาพและแสวงหาวิธีใหม่ๆ ในการปรับปรุงประสบการณ์ผู้ใช้อยู่เสมอ