
React Design Patterns: Các phương pháp hay nhất cho các ứng dụng có khả năng mở rộng
Giới thiệu về các mẫu React Design Patterns
Khi các ứng dụng React phát triển về quy mô và độ phức tạp, việc duy trì mã sạch, hiệu quả và có thể mở rộng trở thành một thách thức. Các mẫu thiết kế React cung cấp các giải pháp đã được chứng minh cho các vấn đề phát triển phổ biến, cho phép các nhà phát triển xây dựng các ứng dụng dễ quản lý và mở rộng hơn. Các mẫu này thúc đẩy tính mô-đun, tái sử dụng mã và tuân thủ các phương pháp hay nhất, khiến chúng trở thành công cụ thiết yếu cho bất kỳ nhà phát triển React nào.
Trong hướng dẫn này, chúng ta sẽ khám phá các mẫu thiết kế React chính, chẳng hạn như Container và Presentation Components , Custom Hooks và Memoization Patterns , với các ví dụ thực tế để chứng minh lợi ích của chúng. Cho dù bạn là người mới bắt đầu hay nhà phát triển có kinh nghiệm, bài viết này sẽ giúp bạn hiểu cách sử dụng các mẫu này để cải thiện quy trình làm việc của mình và tạo ra các ứng dụng React tốt hơn.
Container and Presentation Components
Mẫu Container and Presentation Components là một phương pháp thiết kế được sử dụng rộng rãi trong React, tách logic ứng dụng khỏi việc render UI. Sự tách biệt này giúp tạo ra các thành phần có thể mô-đun hóa, tái sử dụng và kiểm thử được, phù hợp với nguyên tắc tách biệt các mối quan tâm .
- Thành phần Container: Xử lý logic nghiệp vụ, quản lý trạng thái và tìm nạp dữ liệu. Chúng tập trung vào cách mọi thứ hoạt động.
- Thành phần trình bày: Xử lý việc hiển thị dữ liệu và UI. Chúng tập trung vào cách mọi thứ trông như thế nào.
Sự phân chia này giúp cơ sở mã của bạn dễ bảo trì hơn, vì những thay đổi về logic hoặc UI có thể được xử lý độc lập mà không ảnh hưởng lẫn nhau.
Benefits of the Pattern
- Khả năng tái sử dụng mã: Các thành phần trình bày có thể được tái sử dụng ở nhiều phần khác nhau của ứng dụng.
- Khả năng kiểm tra được cải thiện: Logic kiểm tra trở nên dễ dàng hơn vì nó được tách biệt trong các thành phần chứa.
- Bảo trì đơn giản: Những thay đổi về logic hoặc UI có thể được xử lý độc lập, giúp giảm nguy cơ làm hỏng các phần khác của mã.
Ví dụ: Fetching and Displaying User Data
Sau đây là cách triển khai mô hình Container và Presentation Components:
Component Container
import React, { useState, useEffect } from "react";
import UserList from "./UserList";
const UserContainer = () => {
const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch("/api/users")
.then((response) => response.json())
.then((data) => {
setUsers(data);
setLoading(false);
})
.catch(() => setLoading(false));
}, []);
return <UserList users={users} loading={loading} />;
};
export default UserContainer;
Presentation Component
import React from "react";
const UserList = ({ users, loading }) => {
if (loading) return <p>Loading...</p>;
return (
<ul>
{users.map((user) => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
};
export default UserList;
Trong ví dụ này:
UserContainerlấy dữ liệu người dùng và truyền nó cùng với trạng thái tải dưới dạng props tớiUserList.UserListchỉ tập trung vào việc hiển thị dữ liệu người dùng.
Mẫu này tăng cường tính rõ ràng, giảm trùng lặp mã và đơn giản hóa việc thử nghiệm. Nó đặc biệt hữu ích cho các ứng dụng mà việc truy xuất dữ liệu và hiển thị UI thường xuyên và phức tạp.
Custom Hooks for Composition
Custom Hooks cho phép bạn đóng gói logic có thể tái sử dụng, giúp mã React của bạn sạch hơn và có tính mô-đun hơn. Thay vì sao chép logic trên nhiều thành phần, bạn có thể trích xuất nó thành một hook và sử dụng nó ở bất cứ đâu khi cần. Điều này cải thiện khả năng tái sử dụng và khả năng kiểm tra mã trong khi vẫn tuân thủ nguyên tắc DRY (Đừng lặp lại chính mình) .
Ví dụ: Lấy dữ liệu Hook
Móc tùy chỉnh
import { useState, useEffect } from "react";
const useFetchData = (url) => {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch(url)
.then((res) => res.json())
.then((result) => {
setData(result);
setLoading(false);
});
}, [url]);
return { data, loading };
};
export default useFetchData;
Sử dụng Hook
import React from "react";
import useFetchData from "./useFetchData";
const Posts = () => {
const { data: posts, loading } = useFetchData("/api/posts");
if (loading) return <p>Loading...</p>;
return (
<ul>
{posts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
);
};
export default Posts;
Trong ví dụ này, useFetchDatahook đóng gói logic truy xuất dữ liệu, cho phép bất kỳ thành phần nào truy xuất dữ liệu với ít boilerplate nhất. Hook tùy chỉnh rất có giá trị trong việc đơn giản hóa mã và đảm bảo kiến trúc sạch.
Quản lý trạng thái với Reducers
Khi quản lý các trạng thái phức tạp hoặc nhóm lại, Reducer Pattern cung cấp một cách có cấu trúc để xử lý các chuyển đổi trạng thái. Nó tập trung logic trạng thái vào một hàm duy nhất, giúp cập nhật trạng thái có thể dự đoán được và dễ gỡ lỗi hơn. useReducerHook của React là lý tưởng để triển khai mẫu này.
Lợi ích của Reducers
- Khả năng dự đoán: Các thay đổi trạng thái được xác định rõ ràng thông qua các hành động.
- Khả năng mở rộng: Phù hợp với việc quản lý trạng thái phức tạp với nhiều phụ thuộc.
- Khả năng bảo trì: Logic tập trung giúp đơn giản hóa việc gỡ lỗi và thử nghiệm.
Ví dụ: Managing Authentication State
Reducer Function
const initialState = { isAuthenticated: false, user: null };
function authReducer(state, action) {
switch (action.type) {
case "LOGIN":
return { ...state, isAuthenticated: true, user: action.payload };
case "LOGOUT":
return initialState;
default:
return state;
}
}
Thành phần sử dụnguseReducer
import React, { useReducer } from "react";
const AuthComponent = () => {
const [state, dispatch] = useReducer(authReducer, initialState);
const login = () => dispatch({ type: "LOGIN", payload: { name: "John Doe" } });
const logout = () => dispatch({ type: "LOGOUT" });
return (
<div>
{state.isAuthenticated ? (
<>
<p>Welcome, {state.user.name}</p>
<button onClick={logout}>Logout</button>
</>
) : (
<button onClick={login}>Login</button>
)}
</div>
);
};
export default AuthComponent;
Trong ví dụ này:
- Xác
authReducerđịnh trạng thái thay đổi như thế nào dựa trên hành động. - Công
AuthComponentdụnguseReducerđể quản lý trạng thái xác thực.
Bộ giảm đặc biệt hiệu quả trong việc xử lý logic trạng thái phức tạp trong các ứng dụng có khả năng mở rộng, thúc đẩy tính rõ ràng và tính nhất quán trong quản lý trạng thái.
Provider Pattern for Context API
Provider Pattern tận dụng Context API của React để chia sẻ trạng thái hoặc chức năng giữa các thành phần mà không cần prop drilling. Nó bao bọc các thành phần trong một context provider, cho phép các thành phần lồng nhau sâu truy cập dữ liệu được chia sẻ.
Những lợi ích
- Tránh khoan chân vịt: Đơn giản hóa việc truyền dữ liệu qua các thành phần lồng nhau sâu.
- Quản lý trạng thái tập trung: Dễ dàng quản lý các trạng thái toàn cầu như chủ đề hoặc xác thực.
Ví dụ: Theme Context
import React, { createContext, useState, useContext } from "react";
const ThemeContext = createContext();
const ThemeProvider = ({ children }) => {
const [theme, setTheme] = useState("light");
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
{children}
</ThemeContext.Provider>
);
};
const useTheme = () => useContext(ThemeContext);
export { ThemeProvider, useTheme };
Using the Context
import React from "react";
import { ThemeProvider, useTheme } from "./ThemeContext";
const ThemeToggle = () => {
const { theme, setTheme } = useTheme();
return (
<button onClick={() => setTheme(theme === "light" ? "dark" : "light")}>
Switch to {theme === "light" ? "dark" : "light"} mode
</button>
);
};
const App = () => (
<ThemeProvider>
<ThemeToggle />
</ThemeProvider>
);
export default App;
Higher-Order Components (HOC)
Higher-Order Components (HOC) là các hàm lấy một component và trả về một component mới có thêm chức năng. Chúng cho phép bạn tái sử dụng logic trên nhiều component mà không cần sửa đổi cấu trúc của chúng.
Những lợi ích
- Khả năng tái sử dụng mã: Chia sẻ logic như xác thực hoặc chủ đề trên các thành phần.
- Đóng gói: Giữ logic nâng cao tách biệt với thành phần gốc.
Ví dụ: Authentication HOC
const withAuth = (Component) => (props) => {
const isAuthenticated = true; // Replace with actual auth logic
return isAuthenticated ? <Component {...props} /> : <p>Please log in</p>;
};
const Dashboard = () => <div>Welcome to the dashboard</div>;
export default withAuth(Dashboard);
Compound Components
Mẫu Compound Components cho phép bạn xây dựng một thành phần cha với nhiều thành phần con hoạt động cùng nhau một cách gắn kết. Mẫu này lý tưởng để tạo các thành phần UI linh hoạt và có thể tái sử dụng.
Những lợi ích
- Khả năng tùy chỉnh: Các thành phần con có thể được kết hợp theo nhiều cách khác nhau.
- Tính rõ ràng: Xác định rõ ràng mối quan hệ giữa các thành phần cha và con.

Ví dụ: Components Tab
import React, { useState, createContext, useContext } from "react";
const TabsContext = createContext();
const Tabs = ({ children }) => {
const [activeTab, setActiveTab] = useState(0);
return (
<TabsContext.Provider value={{ activeTab, setActiveTab }}>
{children}
</TabsContext.Provider>
);
};
Tabs.Tab = ({ index, label }) => {
const { activeTab, setActiveTab } = useContext(TabsContext);
return (
<button
style={{ fontWeight: activeTab === index ? "bold" : "normal" }}
onClick={() => setActiveTab(index)}
>
{label}
</button>
);
};
Tabs.Panel = ({ index, children }) => {
const { activeTab } = useContext(TabsContext);
return activeTab === index ? <div>{children}</div> : null;
};
// Usage
const App = () => (
<Tabs>
<Tabs.Tab index={0} label="Tab 1" />
<Tabs.Tab index={1} label="Tab 2" />
<Tabs.Panel index={0}>Content for Tab 1</Tabs.Panel>
<Tabs.Panel index={1}>Content for Tab 2</Tabs.Panel>
</Tabs>
);
export default App;
Memoization
Ghi nhớ là một kỹ thuật tối ưu hóa hiệu suất để ngăn chặn việc kết xuất lại các thành phần hoặc tính toán lại các giá trị không cần thiết trong React.
Techniques
- React.memo: Ngăn chặn việc hiển thị lại các thành phần chức năng trừ khi thuộc tính của chúng thay đổi.
const ExpensiveComponent = React.memo(({ value }) => {
console.log("Rendered");
return <p>{value}</p>;
});
export default ExpensiveComponent;
- useMemo: Ghi nhớ kết quả tính toán, chỉ tính toán lại khi sự phụ thuộc thay đổi.
const ExpensiveCalculation = ({ number }) => {
const calculatedValue = React.useMemo(() => number * 2, [number]);
return <p>{calculatedValue}</p>;
};
export default ExpensiveCalculation;
- useCallback: Ghi nhớ các hàm, hữu ích khi truyền lệnh gọi lại cho các thành phần con.
const Parent = () => {
const handleClick = React.useCallback(() => console.log("Clicked!"), []);
return <Child onClick={handleClick} />;
};
const Child = React.memo(({ onClick }) => <button onClick={onClick}>Click</button>);
Ghi nhớ cải thiện hiệu suất trong các tình huống liên quan đến tập dữ liệu lớn hoặc cập nhật giao diện người dùng phức tạp, đảm bảo ứng dụng React luôn phản hồi nhanh.
Phần kết luận
Nắm vững các mẫu thiết kế React là chìa khóa để xây dựng các ứng dụng có khả năng mở rộng, bảo trì và hiệu quả. Bằng cách áp dụng các mẫu như Container và Presentation Components , Custom Hooks và Memoization , bạn có thể hợp lý hóa quá trình phát triển, cải thiện khả năng tái sử dụng mã và nâng cao hiệu suất. Các mẫu nâng cao như Higher-Order Components , Compound Components và Provider Pattern đơn giản hóa hơn nữa việc quản lý trạng thái phức tạp và tương tác thành phần.
Các mẫu này không chỉ mang tính lý thuyết—chúng giải quyết các thách thức thực tế trong quá trình phát triển React, giúp bạn viết mã sạch và có tính mô-đun. Bắt đầu kết hợp các mẫu này vào các dự án của bạn để tạo ra các ứng dụng mạnh mẽ, dễ mở rộng quy mô và có thể bảo trì lâu dài. Với các mẫu thiết kế React trong bộ công cụ của bạn, bạn sẽ được trang bị tốt hơn để giải quyết bất kỳ dự án nào, bất kể phức tạp đến đâu.
Để biết thêm thông tin chi tiết, hãy xem tài liệu về Mẫu thiết kế React trên Patterns.dev .







-
Tagged flutter, mobile-dev, react-native