State trong React là một khía cạnh quan trọng, và việc quản lý state một cách hiệu quả là chìa khóa để giúp cho anh em code React app tốt hơn. Trong bài viết này, chúng ta sẽ tìm hiểu cách sử dụng useContext và useReducer để xây dựng một state management như Redux cho React, và chúng ta cũng sẽ sử dụng TypeScript để giúp cho code clean hơn nhé.
Giới Thiệu
Trước khi bắt đầu, hãy xem qua các khái niệm chính:
- useContext: Một hook trong React giúp chúng ta truy cập giá trị của một Context. Context giúp chia sẻ giá trị giữa các thành phần mà không cần truyền qua nhiều cấp.
- useReducer: Một hook khác giúp quản lý trạng thái ứng dụng thông qua các hành động (actions). Nó cung cấp một cách mạnh mẽ để xử lý logic trạng thái phức tạp.
- TypeScript: Một ngôn ngữ lập trình tĩnh kiểu, giúp bạn xác định rõ kiểu dữ liệu của biến, tham số và giá trị trả về.
Bước 1: Định nghĩa Kiểu và Reducer
Trước tiên, hãy định nghĩa các kiểu TypeScript cho trạng thái và hành động của chúng ta:
// types.ts
export interface CounterState {
count: number;
}
export interface CounterAction {
type: 'INCREMENT' | 'DECREMENT';
}
export interface PageState {
// Định nghĩa các thuộc tính trạng thái khác của trang
}
export interface PageAction {
// Định nghĩa các loại hành động liên quan đến trang
}
export interface AppState {
counter: CounterState;
page: PageState;
}
export type AppAction = CounterAction | PageAction;
Ở đây, chúng ta đã định nghĩa kiểu trạng thái và hành động cho trạng thái đếm (counter
) và một trạng thái trang (page
).
Tiếp theo, chúng ta sẽ tạo các hàm reducer cho từng phần trạng thái:
// reducer.ts
import { CounterState, CounterAction, PageState, PageAction, AppState } from './types';
export const counterReducer = (state: CounterState, action: CounterAction): CounterState => {
switch (action.type) {
case 'INCREMENT':
return { ...state, count: state.count + 1 };
case 'DECREMENT':
return { ...state, count: state.count - 1 };
default:
return state;
}
};
export const pageReducer = (state: PageState, action: PageAction): PageState => {
// Xử lý các thay đổi trạng thái của trang dựa trên các loại hành động
return state;
};
export const mainReducer = (state: AppState, action: AppAction): AppState => {
return {
counter: counterReducer(state.counter, action as CounterAction),
page: pageReducer(state.page, action as PageAction),
};
};
Ở đây, chúng ta đã tạo ra các reducer cho trạng thái đếm và trạng thái trang. Hàm mainReducer
kết hợp tất cả các reducer này để quản lý toàn bộ trạng thái ứng dụng.
Bước 2: Tạo Context và Provider
Tiếp theo, hãy tạo Context và Provider để chia sẻ trạng thái và hành động giữa các thành phần của ứng dụng:
// context.tsx
import React, { createContext, useReducer, useContext, ReactNode } from 'react';
import { AppState, AppAction, mainReducer } from './reducer';
interface AppContextType {
state: AppState;
dispatch: React.Dispatch<AppAction>;
}
const AppContext = createContext<AppContextType | undefined>(undefined);
const initialState: AppState = {
counter: {
count: 0,
},
page: {
// Khởi tạo các thuộc tính trạng thái của trang
},
};
const AppProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
const [state, dispatch] = useReducer(mainReducer, initialState);
return (
<AppContext.Provider value={{ state, dispatch }}>
{children}
</AppContext.Provider>
);
};
const useAppContext = (): AppContextType => {
const context = useContext(AppContext);
if (!context) {
throw new Error('useAppContext phải được sử dụng bên trong AppProvider');
}
return context;
};
export { AppProvider, useAppContext };
Ở đây, chúng ta đã tạo ra một Context để giữ trạng thái và hành động. AppProvider
là một thành phần cung cấp Context cho toàn bộ ứng dụng. Hàm useAppContext
giúp chúng ta dễ dàng truy cập trạng thái và hành động từ bất kỳ thành phần nào.
Bước 3: Sử Dụng Trong Ứng Dụng
Cuối cùng, hãy xem cách chúng ta sử dụng Context và Reducer trong ứng dụng của mình. Dưới đây là một ví dụ đơn giản với một thành phần hiển thị và điều khiển đếm:
// CounterComponent.tsx
import React from 'react';
import { useAppContext } from './context';
const CounterComponent: React.FC = () => {
const { state, dispatch } = useAppContext();
return (
<div>
<p>Đếm: {state.counter.count}</p>
<button onClick={() => dispatch({ type: 'INCREMENT' })}>Tăng</button>
<button onClick={() => dispatch({ type: 'DECREMENT' })}>Giảm</button>
</div>
);
};
Ở đây, chúng ta đã sử dụng useAppContext
để lấy trạng thái và hành động từ Context và hiển thị giá trị đếm. Các nút "Tăng" và "Giảm" sẽ gửi các hành động tương ứng đến reducer.
Kết Luận
Sử dụng useContext
và useReducer
có thể là một cách thay thế đơn giản không cần cài đặt thêm các thư viện khác để quản lý state với những dự án vừa và nhỏ trong React/Nextjs.
Hy vọng bài viết này giúp ích cho anh em =))
Discussion (undefined)