Tiếng Việt

Khám phá hook useEvent của React, công cụ tạo tham chiếu trình xử lý sự kiện ổn định, cải thiện hiệu suất và ngăn chặn render lại không cần thiết.

React useEvent: Đạt được Tham chiếu Trình xử lý Sự kiện Ổn định

Các nhà phát triển React thường gặp phải thách thức khi xử lý các trình xử lý sự kiện (event handler), đặc biệt là trong các kịch bản liên quan đến component động và closure. Hook useEvent, một sự bổ sung tương đối mới cho hệ sinh thái React, cung cấp một giải pháp tinh tế cho những vấn đề này, cho phép các nhà phát triển tạo ra các tham chiếu trình xử lý sự kiện ổn định mà không kích hoạt các lần render lại không cần thiết.

Hiểu về Vấn đề: Sự không ổn định của Trình xử lý Sự kiện

Trong React, các component sẽ render lại khi props hoặc state của chúng thay đổi. Khi một hàm xử lý sự kiện được truyền dưới dạng prop, một thực thể hàm mới thường được tạo ra trong mỗi lần render của component cha. Thực thể hàm mới này, ngay cả khi có cùng logic, vẫn bị React coi là khác biệt, dẫn đến việc render lại component con nhận nó.

Hãy xem xét ví dụ đơn giản sau:


import React, { useState } from 'react';

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

  const handleClick = () => {
    console.log('Clicked from Parent:', count);
    setCount(count + 1);
  };

  return (
    

Count: {count}

); } function ChildComponent({ onClick }) { console.log('ChildComponent rendered'); return ; } export default ParentComponent;

Trong ví dụ này, handleClick được tạo lại mỗi khi ParentComponent render. Mặc dù ChildComponent có thể đã được tối ưu hóa (ví dụ: sử dụng React.memo), nó vẫn sẽ render lại vì prop onClick đã thay đổi. Điều này có thể dẫn đến các vấn đề về hiệu suất, đặc biệt là trong các ứng dụng phức tạp.

Giới thiệu useEvent: Giải pháp

Hook useEvent giải quyết vấn đề này bằng cách cung cấp một tham chiếu ổn định đến hàm xử lý sự kiện. Nó tách biệt một cách hiệu quả trình xử lý sự kiện khỏi chu kỳ render lại của component cha.

Mặc dù useEvent không phải là một hook có sẵn trong React (tính đến React 18), nó có thể dễ dàng được triển khai như một hook tùy chỉnh hoặc, trong một số framework và thư viện, được cung cấp như một phần của bộ tiện ích của họ. Dưới đây là một cách triển khai phổ biến:


import { useCallback, useRef, useLayoutEffect } from 'react';

function useEvent any>(fn: T): T {
  const ref = useRef(fn);

  // UseLayoutEffect rất quan trọng ở đây để cập nhật đồng bộ
  useLayoutEffect(() => {
    ref.current = fn;
  });

  return useCallback(
    (...args: Parameters): ReturnType => {
      return ref.current(...args);
    },
    [] // Mảng dependency được cố tình để trống, đảm bảo sự ổn định
  ) as T;
}

export default useEvent;

Giải thích:

Sử dụng useEvent trong Thực tế

Bây giờ, hãy tái cấu trúc ví dụ trước đó bằng cách sử dụng useEvent:


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

function useEvent any>(fn: T): T {
  const ref = useRef(fn);

  // UseLayoutEffect rất quan trọng ở đây để cập nhật đồng bộ
  useLayoutEffect(() => {
    ref.current = fn;
  });

  return useCallback(
    (...args: Parameters): ReturnType => {
      return ref.current(...args);
    },
    [] // Mảng dependency được cố tình để trống, đảm bảo sự ổn định
  ) as T;
}

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

  const handleClick = useEvent(() => {
    console.log('Clicked from Parent:', count);
    setCount(count + 1);
  });

  return (
    

Count: {count}

); } function ChildComponent({ onClick }) { console.log('ChildComponent rendered'); return ; } export default ParentComponent;

Bằng cách bọc handleClick với useEvent, chúng ta đảm bảo rằng ChildComponent nhận cùng một tham chiếu hàm qua các lần render của ParentComponent, ngay cả khi state count thay đổi. Điều này ngăn chặn các lần render lại không cần thiết của ChildComponent.

Lợi ích của việc sử dụng useEvent

Các trường hợp sử dụng useEvent

Các phương án thay thế và Lưu ý

Mặc dù useEvent là một công cụ mạnh mẽ, có những phương pháp tiếp cận thay thế và những điều cần lưu ý:

Lưu ý về Quốc tế hóa và Khả năng tiếp cận

Khi phát triển các ứng dụng React cho đối tượng toàn cầu, việc xem xét quốc tế hóa (i18n) và khả năng tiếp cận (a11y) là rất quan trọng. Bản thân useEvent không ảnh hưởng trực tiếp đến i18n hay a11y, nhưng nó có thể gián tiếp cải thiện hiệu suất của các component xử lý nội dung được bản địa hóa hoặc các tính năng về khả năng tiếp cận.

Ví dụ, nếu một component hiển thị văn bản được bản địa hóa hoặc sử dụng các thuộc tính ARIA dựa trên ngôn ngữ hiện tại, việc đảm bảo các trình xử lý sự kiện trong component đó ổn định có thể ngăn chặn các lần render lại không cần thiết khi ngôn ngữ thay đổi.

Ví dụ: useEvent với Bản địa hóa


import React, { useState, useContext, createContext, useCallback, useRef, useLayoutEffect } from 'react';

function useEvent any>(fn: T): T {
  const ref = useRef(fn);

  // UseLayoutEffect rất quan trọng ở đây để cập nhật đồng bộ
  useLayoutEffect(() => {
    ref.current = fn;
  });

  return useCallback(
    (...args: Parameters): ReturnType => {
      return ref.current(...args);
    },
    [] // Mảng dependency được cố tình để trống, đảm bảo sự ổn định
  ) as T;
}

const LanguageContext = createContext('en');

function LocalizedButton() {
  const language = useContext(LanguageContext);
  const [text, setText] = useState(getLocalizedText(language));

  const handleClick = useEvent(() => {
    console.log('Button clicked in', language);
    // Perform some action based on the language
  });

  function getLocalizedText(lang) {
      switch (lang) {
        case 'en':
          return 'Click me';
        case 'fr':
          return 'Cliquez ici';
        case 'es':
          return 'Haz clic aquí';
        default:
          return 'Click me';
      }
    }

    //Mô phỏng thay đổi ngôn ngữ
    React.useEffect(()=>{
        setTimeout(()=>{
            setText(getLocalizedText(language === 'en' ? 'fr' : 'en'))
        }, 2000)
    }, [language])

  return ;
}

function App() {
  const [language, setLanguage] = useState('en');

  const toggleLanguage = useCallback(() => {
    setLanguage(language === 'en' ? 'fr' : 'en');
  }, [language]);

  return (
    
      
); } export default App;

Trong ví dụ này, component LocalizedButton hiển thị văn bản dựa trên ngôn ngữ hiện tại. Bằng cách sử dụng useEvent cho trình xử lý handleClick, chúng ta đảm bảo rằng nút không bị render lại một cách không cần thiết khi ngôn ngữ thay đổi, giúp cải thiện hiệu suất và trải nghiệm người dùng.

Kết luận

Hook useEvent là một công cụ quý giá cho các nhà phát triển React muốn tối ưu hóa hiệu suất và đơn giản hóa logic của component. Bằng cách cung cấp các tham chiếu trình xử lý sự kiện ổn định, nó ngăn chặn các lần render lại không cần thiết, cải thiện khả năng đọc mã nguồn và nâng cao hiệu quả tổng thể của các ứng dụng React. Mặc dù nó không phải là một hook có sẵn trong React, cách triển khai đơn giản và những lợi ích đáng kể của nó làm cho nó trở thành một sự bổ sung đáng giá vào bộ công cụ của bất kỳ nhà phát triển React nào.

Bằng cách hiểu các nguyên tắc đằng sau useEvent và các trường hợp sử dụng của nó, các nhà phát triển có thể xây dựng các ứng dụng React hiệu suất cao hơn, dễ bảo trì hơn và có khả năng mở rộng tốt hơn cho đối tượng toàn cầu. Hãy nhớ luôn đo lường hiệu suất và xem xét các nhu cầu cụ thể của ứng dụng của bạn trước khi áp dụng các kỹ thuật tối ưu hóa.