Hướng dẫn React Hooks với TypeScript – Hướng dẫn dùng các tính năng mới qua ví dụ dễ hiểu

React Hooks cách dùng các hàm thông dụng cùng với Typescript

Hooks được thêm vào React 16.8 tại hội thảo React 2018. Tính năng mới bổ sung khá tuyệt vời giúp cho Functional Component hay Stateless Component có thể làm mọi thứ mà Class Component làm được.

Bài viết mình tóm gọn nên phù hợp cho các bạn đã biết React nhé ❤️. Sau đây mình tóm tắt nhanh cách dùng một số hàm chính của React Hook: useState, useEffect, useRef, useMemo, useCallback.

useState

useState đây là hàm mà bạn sẽ dùng rất nhiều. Thay vì dùng this.state từ class components, bạn có thể truy cập state hiện tại với 1 giá trị khởi tạo nó và 1 hàm gọi để thay đổi state.

Ví dụ:

import React from 'react';

// Khởi tạo props initial = 0
const Counter:FC<{ initial?: number }> = ({ initial = 0 }) => {

  // clicks là state, setClicks là hàm setState cho state clicks
  const [clicks, setClicks] = React.useState(initial);
  return <>
    <p>Clicks: {clicks}</p>
    <button onClick={() => setClicks(clicks+1)}>+</button>
    <button onClick={() => setClicks(clicks-1)}>-</button>
  </>
}

useEffect

useEffect liên quan tới side effects. Việc thêm event listeners, thay đổi document, fetching data… sẽ ảnh hưởng tới React lifecycle component (componentDidUpdatecomponentDidMountcomponentWillUnmount). useEffect dùng để tối ưu hóa việc gọi hàm và clean up chúng. Các hàm này chỉ có trong class component. useEffect giúp bạn làm điều này!  Phương thức này có 2 tham số:

  • 1 hàm anonymous không có tham số.
  • 1 mảng chứa tham số, hàm trên chỉ được gọi khi tham số này thay đổi.
// Giả sử có state là name. document.title sẽ đổi khi name thay đổi
const [name, setName] = React.useState('Stefan');
useEffect(() => {
  document.title = `Hello ${name}`;
}, [name])

Không có hàm componentWillUnmount cho functional component. Làm thế nào để clean up ?

Việc return sẽ thực hiện hàm removeEventListener sẽ tương tự cho componentWillUnmount.

React.useEffect(() => {
  const handler = () => {
    document.title = window.width;
  }
  window.addEventListener('resize', handler);

  // ⚡️ sẽ không compile
  return true;

  // ✅  sẽ compile
  return () => {
    window.removeEventListener('resize', handler);
  }
})

useContext

useContext cho phép bạn truy cập thuộc tính context bất cứ đâu trong components. Nó tương tự Context.Consumer trong class components:

import React, { useContext } from 'react';

// Khởi tạo đối tượng context lang với type là string
export const LanguageContext = React.createContext({ lang: 'en' });

const Display = () => {
  // Gọi lang với type là string
  const { lang } = useContext(LanguageContext);
  return <>
    <p>Your selected language: {lang}</p>
  </>
}

useRef

Giờ đây bạn có thể dùng useRef sẽ thay thế cho this.myRef = React.createRef() trong class component. useRef dùng để truy cập các đối tương bên trong HTML Node.

import React from 'react';
function TextInputWithFocusButton() {
  // Thường khởi tạo refs với giá trị null
  const inputRef = React.useRef(null);
  const onButtonClick = () => {
    // (?) Typescript sẽ quăng lỗi vì inputRef có thể null. Fix code bên dưới nhé
    inputRef.current.focus();
  };
  return (
    <>
      <input ref={inputRef} type="text" />
      <button onClick={onButtonClick}>Focus the input</button>
    </>
  );
}

Lưu ý với typescript:

  • Phải khởi tạo refs là null.
  • inputRef có thể null. TypeScript cần check là inputRef.current not null mới cho gọi hàm focus().
function TextInputWithFocusButton() {
  // Báo cho typescipt biết type cụ thể của refs là HTMLInputElement
  const inputRef = useRef<HTMLInputElement>(null);
  const onButtonClick = () => {
    // Kiểm tra inputRef tồn tại trước khi dùng hàm bên trong nó
    if(inputRef && inputRef.current) {
      inputRef.current.focus();
    } 
  };
  return (
    <>
      <input ref={inputRef} type="text" />
      <button onClick={onButtonClick}>Focus the input</button>
    </>
  );
}

useMemo

useMemo cũng tương tự useEffect nó chỉ chạy khi có tham số thay đổi. useMemo dùng như là pure function để tạo 1 phần HTML cho component.

Lưu ý khi dùng với TypeScript, useMemo sẽ trả về type object giống với hàm bên trong trả về.

const getStatus = React.useMemo(() => (status ? <Tag className={statusClass}>{status}</Tag> : ''), [status]);

render(){
 return <div>{getStatus}</div>
}

Trong ví dụ trên: getStatussẽ trả về JSX.Element do hàm anonymous bên trong trả về JSX.Element. Hàm này chỉ render lại khi status thay đổi.

useCallback

useCallback cũng tương tự useMemo. Nhưng nó trả về callback function, not a value.

const memoCallback = React.useCallback((a: number) => {
  // doSomething
}, [a])

// ⚡️ Không compile, vì không có tham số.
memoCallback();

// ✅ Compile
memoCallback(3);

Xong. Mình đã hướng dẫn bạn cách dùng một số hàm thông dụng trong React Hooks. Bạn có thắc mắc gì thì đừng ngần ngại mà bình luận dưới nhé!