Hook useDebounce trong React
Tối ưu hóa đầu vào của người dùng với Debouncing trong React thông qua Hook tùy chỉnh có thể tái sử dụng
Khi xử lý đầu vào của người dùng, đặc biệt trong các trường tìm kiếm hoặc trong các cuộc gọi API, việc cập nhật trạng thái mỗi lần người dùng gõ phím có thể dẫn đến các vấn đề về hiệu suất. Một hàm debounce giúp trì hoãn việc thực thi cho đến khi một khoảng thời gian xác định trôi qua kể từ lần nhập cuối cùng.
Trong bài viết này, chúng ta sẽ bắt đầu bằng cách tạo một thành phần mà không sử dụng hook debounce tùy chỉnh, sau đó refactor thành phần đó để sử dụng hook useDebounce có thể tái sử dụng.
Triển khai Debouncing mà không sử dụng Hook Tùy Chỉnh
Chúng ta bắt đầu bằng cách tạo một thành phần React đơn giản để xử lý đầu vào của người dùng theo thời gian thực mà không sử dụng debouncing.
import React, { useState, useEffect } from "react";
const App = () => {
const [query, setQuery] = useState("");
const [results, setResults] = useState([]);
useEffect(() => {
if (query) {
fetch(`https://demo.dataverse.org/api/search?q=${query}`)
.then((res) => res.json())
.then(({ data }) => setResults(data.items));
}
}, [query]);
return (
<div style={{ textAlign: "center", marginTop: "50px" }}>
<h2>Search</h2>
<input
type="text"
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="Type to search..."
/>
<ul>
{results.map((item, index) => (
<li key={index}>{item.name}</li>
))}
</ul>
</div>
);
};
export default App;
Các vấn đề với cách tiếp cận này
Mặc dù thành phần hoạt động, nhưng nó có một vài nhược điểm:
- Lượng gọi API quá nhiều: Một yêu cầu được gửi đi mỗi khi người dùng gõ phím, dẫn đến việc gọi API không cần thiết.
- Vấn đề về hiệu suất: Việc cập nhật trạng thái và render lại giao diện quá thường xuyên có thể làm chậm UI.
- Thiếu kiểm soát: Không có kiểm soát về thời điểm nào tìm kiếm được kích hoạt.
Tạo một Hook useDebounce
Tùy Chỉnh
Để cải thiện hiệu suất, chúng ta sẽ tạo một hook useDebounce để trì hoãn việc cập nhật trạng thái cho đến khi người dùng dừng gõ trong một khoảng thời gian nhất định.
Hook useDebounce
Dưới đây là cách tạo một hook tùy chỉnh để debounce một giá trị:
import { useState, useEffect } from "react";
const useDebounce = (value, delay) => {
const [debouncedValue, setDebouncedValue] = useState(value);
useEffect(() => {
const handler = setTimeout(() => {
setDebouncedValue(value);
}, delay);
return () => {
clearTimeout(handler);
};
}, [value, delay]);
return debouncedValue;
};
export default useDebounce;
Lợi ích của Hook useDebounce
- Giảm số lần gọi API: Yêu cầu chỉ được gửi đi khi người dùng ngừng gõ, giúp giảm các cuộc gọi không cần thiết.
- Cải thiện hiệu suất: Ngăn chặn việc cập nhật trạng thái nhiều lần và render lại UI không cần thiết.
- Tái sử dụng: Hook useDebounce có thể được tái sử dụng trong nhiều thành phần khác nhau.
Refactor thành phần để sử dụng useDebounce
Bây giờ chúng ta sẽ refactor thành phần tìm kiếm để sử dụng hook useDebounce
.
import React, { useState, useEffect } from "react";
import useDebounce from "./useDebounce";
const SearchBar = () => {
const [query, setQuery] = useState("");
const debouncedQuery = useDebounce(query, 500);
const [results, setResults] = useState([]);
useEffect(() => {
if (debouncedQuery) {
fetch(`https://demo.dataverse.org/api/search?q=${query}`)
.then((res) => res.json())
.then(({ data }) => setResults(data.items));
}
}, [debouncedQuery]);
return (
<div style={{ textAlign: "center", marginTop: "50px" }}>
<h2>Search</h2>
<input
type="text"
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="Type to search..."
/>
<ul>
{results.map((item, index) => (
<li key={index}>{item.name}</li>
))}
</ul>
</div>
);
};
export default SearchBar;
Lợi ích chính của việc Refactor này
- Ngăn chặn các cuộc gọi API không cần thiết: Yêu cầu chỉ được gửi đi khi người dùng ngừng gõ, tránh các cuộc gọi API dư thừa.
- Cải thiện hiệu suất: Việc cập nhật trạng thái và render lại UI được giảm thiểu, cải thiện phản hồi của giao diện người dùng.
- Mã sạch hơn: Hook useDebounce giúp giữ logic của thành phần đơn giản và dễ bảo trì.
Các trường hợp sử dụng khác của useDebounce
Hook useDebounce có thể được sử dụng trong nhiều tình huống khác nhau. Dưới đây là một vài ví dụ:
1. Debouncing các trường nhập trong Form
Khi xử lý đầu vào form, bạn có thể muốn debounce đầu vào để tránh các xác thực hoặc hành động không cần thiết được thực hiện trên mỗi lần gõ phím.
import React, { useState } from "react";
import useDebounce from "./useDebounce";
const DebouncedInput = () => {
const [input, setInput] = useState("");
const debouncedInput = useDebounce(input, 500);
return (
<div style={{ textAlign: "center", marginTop: "50px" }}>
<h2>Debounced Input</h2>
<input
type="text"
value={input}
onChange={(e) => setInput(e.target.value)}
placeholder="Type something..."
/>
<p>Processed Value: {debouncedInput}</p>
</div>
);
};
export default DebouncedInput;
2. Xử lý sự kiện thay đổi kích thước cửa sổ
Debouncing cũng hữu ích để tối ưu hóa các thao tác tốn kém được kích hoạt bởi sự kiện thay đổi kích thước cửa sổ, chẳng hạn như tính toán lại bố cục hoặc cập nhật các thành phần UI.
import React, { useState, useEffect } from "react";
import useDebounce from "./useDebounce";
const ResizeComponent = () => {
const [width, setWidth] = useState(window.innerWidth);
const debouncedWidth = useDebounce(width, 300);
useEffect(() => {
const handleResize = () => setWidth(window.innerWidth);
window.addEventListener("resize", handleResize);
return () => window.removeEventListener("resize", handleResize);
}, []);
return <p>Window Width: {debouncedWidth}px</p>;
};
export default ResizeComponent;
3. Tự động lưu dữ liệu
Bạn có thể sử dụng debouncing cho chức năng tự động lưu trong form hoặc trình soạn thảo văn bản để giảm thiểu các lần lưu dữ liệu khi người dùng gõ.
import React, { useState, useEffect } from "react";
import useDebounce from "./useDebounce";
const AutoSaveComponent = () => {
const [text, setText] = useState("");
const debouncedText = useDebounce(text, 1000);
useEffect(() => {
if (debouncedText) {
console.log("Auto-saving:", debouncedText);
// Perform save operation here
}
}, [debouncedText]);
return (
<textarea
value={text}
onChange={(e) => setText(e.target.value)}
placeholder="Start typing..."
/>
);
};
export default AutoSaveComponent;
4. Lọc dữ liệu lớn
Debouncing rất hữu ích khi lọc các tập dữ liệu lớn, vì nó giảm thiểu việc render lại không cần thiết và cải thiện hiệu suất.
import React, { useState } from "react";
import useDebounce from "./useDebounce";
const items = Array.from({ length: 10000 }, (_, i) => `Item ${i + 1}`);
const FilterComponent = () => {
const [filter, setFilter] = useState("");
const debouncedFilter = useDebounce(filter, 400);
const filteredItems = items.filter((item) =>
item.toLowerCase().includes(debouncedFilter.toLowerCase())
);
return (
<div>
<input
type="text"
value={filter}
onChange={(e) => setFilter(e.target.value)}
placeholder="Filter items..."
/>
<p>Results: {filteredItems.length}</p>
</div>
);
};
export default FilterComponent;
Cách tiếp cận này giảm thiểu việc hiển thị lại giao diện người dùng không cần thiết bằng cách giảm tần suất lọc.
Phần kết luận
Trong bài viết này, tôi đã chứng minh cách debouncing user input có thể cải thiện đáng kể hiệu suất trong các ứng dụng React. Bằng cách triển khai useDebounce
custom hook, chúng ta có thể tối ưu hóa các lệnh gọi API, cập nhật trạng thái và hiển thị lại UI. useDebounceHook
rất linh hoạt và có thể áp dụng trong nhiều tình huống, chẳng hạn như xử lý các đầu vào tìm kiếm, xác thực biểu mẫu, sự kiện thay đổi kích thước cửa sổ và lọc các tập dữ liệu lớn. Sử dụng mẫu này sẽ mang lại trải nghiệm hiệu quả hơn, phản hồi nhanh hơn và thân thiện với người dùng hơn.
Cảm ơn các bạn đã theo dõi!
All rights reserved