Sử dụng react hook

Sử dụng react hook để tạo state, tối ưu hiệu năng hoạt động cho các function component trong ứng dụng của bạn và nhiều lợi ích khác.

Sử dụng react hook

Hook là cách thức nâng cao khả năng cho các function component , giúp các function component truy xuất đến state và các method lifecycle component khác.

Hooks chỉ có thể gọi bên trong các function component, hook không làm việc trong class components.

Để dùng hook, bạn gọi chúng ở top của component . Một số hook thường dùng trong React đó là useState, useReducer,  useRef, useContext, useEffect, useMemo, useCallback, useLayoutEffect

useState hook

Khi sử dụng react hook , bạn dùng useState là nhiều nhất. useState là hook giúp bạn khai báo biến trong state của function component. Cú pháp như sau:

const [ tenBien , hamGan ] = useState( giatri );

Trong đó tenBien là tên của biến state mà bạn muốn có, hamGan là tên của hàm sẽ dùng để gọi khi cần gán giá trị mới cho biến,  giatri là giá trị khởi đầu gán cho tenBien.

Để dùng useState trong  mỗi component , bạn import useState ở đầu component và dùng cí pháp như trên để tạo các biến state. Biến state có thể có kiểu bất kỳ như string, number, array, object…

Khai báo và sử dụng useState hook

//src/MyComponent.js
import { useState } from 'react';
export function MyComponent() {
  const [hoten, ganHoTen] = useState( 'Tèo' );
  const [email, ganEmail] = useState('teo@gmail.com');
  const [diem, ganDiem] = useState( [ 7 , 6 , 9]  );
  console.log(hoten, email,  diem);
}

Hiện các giá trị của biến state

Bạn có thể truy xuất các biến state đã khai báo trong component một cách bình thường thông qua tên biến , không có gì đặc biệt.

//src/MyComponent.js
import { useState } from 'react';
export function MyComponent() {
  const [hoten, ganHoTen] = useState( 'Tèo' );
  const [email, ganEmail] = useState('teo@gmail.com');
  const [diem, ganDiem] = useState( [ 7 , 6 , 9]  );
  return (
    <div>
        <p>Họ tên :  <b> {hoten} </b> </p> 
        <p>Email :  <b> {email} </b> </p> 
        <p>Điểm :  <b> {diem.join(" , ")} </b> </p> 
    </div>
  )
}

Gán giá trị mới cho biến state

Để gán giá trị cho các biến state đã khai báo, bạn gọi hàm gán lúc định nghĩa biến. Ví dụ ganHoTen(‘Minh’); hay ganDiem ([8, 7, 5, 6] );

//src/MyComponent.js
import { useState } from 'react';
export function MyComponent() {
  const [hoten, ganHoTen] = useState( 'Tèo' );
  const [email, ganEmail] = useState('teo@gmail.com');
  const [diem, ganDiem] = useState( [ 7 , 6 , 9]  );
  return (
    <div>
        <p>Họ tên :  <b>{hoten}</b> </p> 
        <p>Email :  <b>{email}</b> </p> 
        <p>Điểm :  <b>{diem.join(" , ")}</b> </p> 
        <button onClick={()=> ganHoTen('Minh')}> Gán họ tên</button>
        <button onClick={()=> ganDiem ([8, 7, 5, 6] )}> Gán điểm</button>
    </div>
  )
}

Chú ý: Trong Strict Mode, React thường sẽ gọi 2 lần lệnh gán giá trị ban đầu, lệnh gán giá trị mới. Muốn bỏ thì vào index.js bỏ tag <React.StrictMode> đang bao quanh <App>

useEffect hook

useEffect là 1 hook trong react giúp component của bạn liên lạc hiệu quả với các hệ thống bên ngoài React (ví dụ gọi API đến phần backend, tương tác với ứng dụng bên thứ ba…) .

Khi cần dùng, bạn import useEffect và sử dụng cú pháp như sau :

useEffect(setup, dependencies?)

setup: là hàm xử lý logic nghiệp vụ gì đó của bạn tùy mục đích sử dụng.  Trong hàm setup này, nếu muốn thì bạn có thể trả về 1 hàm cleanup. Khi component được thêm vào DOM thì React sẽ chạy hàm setup này. Mỗi lần render lại với các thay đổi của dependencies, React trước tiên sẽ chạy hàm cleanup (nếu có) để bỏ các giá trị cũ, và rồi chạy lại hàm setup với các giá trị mới.  Khi component bọ gỡ khỏi DOM thì React cũng sẽ chạy hàm cleanup.

dependencies là phần optional có thể bỏ qua. Đây là nơi liệt kê các props, state, variables, functions đang có trong component. Ví dụ [dep1, dep2, dep3]. React sẽ so sánh giá trị của mỗi dependency với giá trị trước, nếu có thay đổi thì mới chạy code setup. Nếu bỏ qua tham số này thì code Effect sẽ chạy lại mỗi lần render lại component.

import { useEffect } from 'react';
import { createConnection } from './chat.js';

function ChatRoom({ roomId }) {
  const [serverUrl, setServerUrl] = useState('https://localhost:1234');
  useEffect(() => {
    const connection = createConnection(serverUrl, roomId);
    connection.connect();
    return () => { connection.disconnect();
 };
  }, [serverUrl, roomId] );
  // ...
}

Chú ý: Nếu không đồng bộ gì với bên ngoài thì không cần thiết dùng Effect.

Ví dụ bên dưới đang theo dõi (dependency) là biến hoten. Khi giá trị của họ tên thay đổi thì hàm setup sẽ chạy, và hàm setup nếu có return 1 hàm (gọi là hàm cleanup) thì nó sẽ chạy trước để xóa giá trị cũ rồi setup mới chạy lại với các giá trị mới.

//src/MyComponent.js
import { useState, useEffect } from 'react';
export function MyComponent() {
const [hoten, ganHoTen] = useState( 'Tèo' );
const [email, ganEmail] = useState('teo@gmail.com');
useEffect (
() => {
console.log("Hàm setup chạy")
return () => { console.log("Xóa hoten"); }

} ,
[ hoten]
);
return (
<div>
<p>Họ tên: <b>{hoten}</b> Email: <b>{email}</b> </p>
<button onClick={()=> ganHoTen('Minh')}> Gán họ tên</button>
<button onClick={()=> ganEmail('minh@yahoo.com')}> Gán Email </button>
</div>
)
}
export default MyComponent;

useLayoutEffect hook

useLayoutEffect gần giống như useEffect, dùng ít hơn useEffect. Hàm này chạy trước useEffect, nó fire trước khi trình duyệt repaint màn hình, trong các trường hợp liên quan đến lấy độ lớn, tọa độ.

useLayoutEffect(setup, dependencies?)
import { useState, useRef, useLayoutEffect } from 'react';
function Tooltip() {
  const ref = useRef(null);
  const [tooltipHeight, setTooltipHeight] = useState(0);

  useLayoutEffect(() => {
    const { height } = ref.current.getBoundingClientRect();
    setTooltipHeight(height);
  }, [ ]);
  // ...
//App.js
import { useEffect, useLayoutEffect, useState, useRef } from 'react';
function App() {
useEffect(() => console.log("useEffect", x) )
useLayoutEffect(() => console.log("useLayoutEffect", x))
const [x, ganx] = useState(10)
return (
<div className="App"> { console.log('Khi render.', x)}
<button onClick={ () => ganx(x+1) }> Tăng x</button>
</div>
)}
export default App;

useRef hook

useRef là React Hook cho phép bạn reference 1 giá trị mà không cần render. Cú pháp như sau:

const ref = useRef(initialValue)

Trong đó initialValue là giá trị có kiểu bất kỳ khởi đầu

import { useRef } from 'react';
function MyComponent() {
const intervalRef = useRef(0);
const inputRef = useRef(null);
// ...

useRef trả về 1 object với 1 thuộc tính là current . Ban đầu current là initalValue, sau đó thì có thể gán giá trị khác. Nếu bạn pass ref object cho thuộc tính của 1 JSX node thì React sẽ gán current cho nó. Trong lần render kế tiếp, useRef vẫn trả về same object. Khi thay đổi giá trị trong ref sẽ không kích hoạt render lại component. Bằng cách dùng ref, bạn có thể :

  • Lưu trữ thông tin giữa các lần re-renders
  • Đổi giá trị mà không trigger re-render compoment
  • Tạo các thông tin riêng cho mỗi component

Có thể dùng ref để quản lý DOM. Đầu tiên bạn khai báo 1 ref với giá trị ban đầu tùy ý.

import { useRef } from 'react';
function MyComponent() {
const inputRef = useRef(null);
// ...

Kế đó là đưa ref JSX như là thuộc tính ref của 1 node

<input ref={inputRef} />;

React sẽ gán current của ref tới DOM node. Để cho bạn truy xuất đến DOM node thông qua ref. Ví dụ:

function handleClick() { inputRef.current.focus(); }

Ví dụ sau sử dụng 2 cách lấy giá trị trong input, trong đó có cách dùng ref. Component được render nhiều lần (khi thay đổi inputValue) nhưng giá trị trong ref vẫn giữ nguyên.

//src/MyComponent.js
import React from 'react';
import { useState, useRef } from 'react';
export function MyComponent() {
const [inputValue, setInputValue] = useState();
const countRef = useRef(-1);
countRef.current = countRef.current + 1;
const htRef = useRef(null);
function thongtin() {
console.log(htRef.current.value);
htRef.current.style.color='red';
htRef.current.focus();
}
return (
<form>
<p>Nhập số <input onChange=
{(e)=>setInputValue(e.target.value)}/></p>
<p>Họ tên<input ref={htRef} /></p>
<h1>Đếm: {countRef.current} </h1>
<button type='button'
onClick={()=>thongtin()}>Thông tin</button>
</form>
)
}
export default MyComponent;

useMemo

useMemo là một React Hook dùng để lưu lại giá trị 1 kết quả tính toán nào đó để không cần tính toán lại nhiều lần trong các lần render của component. useMemo Hook chỉ chạy khi có một trong các dependency có thay đổi . Cú pháp như sau:

const cachedValue = useMemo( hàmTinhToán, dependencies)
import { useMemo } from 'react';
function TodoList({ todos, tab }) {
const visibleTodos = useMemo(
() => filterTodos(todos, tab), [todos, tab]
);
// ...
}
  • hàmTinhToán: là hàm sẽ chạy trả về kết quả nào đó và kết quả sẽ được lưu lại. Trong lần render kế , React sẽ quan sát nếu thấy dependency không thay đổi giá trị thì không chạy hàm mà dùng giá trị đã tính trước đó. Ngược lại nếu có thay đổi thì hàmTinhTóan sẽ chạy lại.
  • dependencies: là mảng các biến cần theo dõi , có thể là props, state, variables, functions trong component.
//App.js
import { MyComponent } from './MyComponent';
function App() { return (<MyComponent /> ); }
export default App;
//src/MyComponent.js
import { useState, useMemo } from 'react';
const tinhtoan = (num) => {
console.log("Tính toán...");
let kq = 0;
for (let i=0; i<100000; i++) kq+= (num+i)*(num+i);
return kq;
}
export function MyComponent() {
const [count, setCount] = useState(0);
const [data, setData] = useState([]);
const giatri = useMemo(() => tinhtoan(count), [count]);
const tangDem = () => setCount( c => c + 1) ;
const add = () => setData( t => [...t, "Chào "]);
return (
<div>
<div>
{data.map((d, index)=><span key={index}>{d}</span>)}
<button onClick={add}>Thêm</button>
</div> <hr />
<div>
Đếm: {count} <button onClick={tangDem}>+</button>
<h2>Kết quả: {giatri.toLocaleString("vi")}</h2>
</div>
</div>
)
}
export default MyComponent;

Việc sử dụng react hook rất quan trọng khi bạn dùng component dạng function. Muốn xem thêm về các React Hook thì xem thêm ở đây : https://react.dev/reference/react .

Đối với các tiện ích dành cho class component thì bạn xem bài này nhé: Vòng đời của component trong React