Chào mừng các bạn đến với bài số 3 của khoá học Next.js! Trong bài này, chúng ta sẽ tìm hiểu cách sử dụng Redux với Next.js để quản lý trạng thái ứng dụng.
Xem bài trước: Next.js bài 2: Thực hiện xác thực người dùng với Next.js và Axios
Như bạn đã biết, quản lý trạng thái ứng dụng là một trong những thách thức lớn nhất trong phát triển ứng dụng web. Nếu không quản lý tốt trạng thái của ứng dụng, nó sẽ dễ dàng bị lỗi, khó bảo trì và không đáp ứng được yêu cầu của người dùng. Đây là lý do tại sao Redux đã trở thành một công cụ quan trọng trong việc quản lý trạng thái ứng dụng.
Với Redux, chúng ta có thể tạo ra một “store” để lưu trữ trạng thái của ứng dụng và sử dụng các “action” để thay đổi trạng thái đó. Khi trạng thái của ứng dụng thay đổi, Redux sẽ thông báo cho các thành phần khác trong ứng dụng để cập nhật lại giao diện.
Cài đặt redux
Để bắt đầu sử dụng Redux với Next.js, chúng ta cần cài đặt các gói phụ thuộc sau:
npm install redux react-redux next-redux-wrapper
Sau khi cài đặt xong, chúng ta bắt đầu bằng việc khởi tạo file store.js để quản lý trạng thái cho toàn bộ ứng dụng.
Khởi tạo store
Trong thư mục src tạo file store.js và thêm code sau:
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers';
const initialState = {};
const middleware = [thunk];
const store = createStore(
rootReducer,
initialState,
applyMiddleware(...middleware)
);
export default store;
Trong đó, chúng ta import createStore, applyMiddleware, thunk và rootReducer từ các modules tương ứng. Sau đó, chúng ta khởi tạo initialState và middleware cho ứng dụng. Cuối cùng, chúng ta sử dụng createStore để tạo store với rootReducer, initialState và middleware.
Khởi tạo rootReducer
Tiếp theo, chúng ta sẽ tạo file reducers/index.js và khởi tạo rootReducer.
import { combineReducers } from 'redux';
import authReducer from './authReducer';
import todoReducer from './todoReducer';
const rootReducer = combineReducers({
auth: authReducer,
todo: todoReducer
});
export default rootReducer;
Chúng ta sử dụng combineReducers để kết hợp nhiều reducers vào. rootReducer. Trong ví dụ của chúng ta, chúng ta có hai reducers: authReducer và todoReducer.
Khởi tạo reducers
Trong thư mục reducers, chúng ta tạo hai file: authReducer.js và todoReducer.js. Bây giờ chúng ta sẽ xây dựng reducer cho auth.
import { LOGIN_SUCCESS, LOGIN_FAILURE, LOGOUT } from '../actions/types';
const initialState = {
isAuthenticated: false,
user: null,
error: null
};
export default function authReducer(state = initialState, action) {
switch (action.type) {
case LOGIN_SUCCESS:
return {
...state,
isAuthenticated: true,
user: action.payload,
error: null
};
case LOGIN_FAILURE:
return {
...state,
isAuthenticated: false,
user: null,
error: action.payload
};
case LOGOUT:
return {
...state,
isAuthenticated: false,
user: null,
error: null
};
default:
return state;
}
}
Chúng ta khởi tạo. initialState cho reducer với. isAuthenticated` được đặt là. false`, user và error được đặt là null. Reducer xử lý các action để cập nhật store.
Chúng ta khởi tạo. initialState cho reducer với. isAuthenticated` được đặt là false, user và error được đặt là null. Reducer xử lý các action để cập nhật store.
Hãy tạo một file mới có tên là. todos.js trong thư mục. reducers. Trong file này, đầu tiên ta sẽ tạo ra initialState của todos như sau:
const initialState = {
list: [],
loading: false,
error: null,
};
Ở đây, ta khởi tạo state cho todo với list rỗng, loading bằng false, và error bằng null.
Tiếp theo, ta sẽ tạo reducer cho todo. Trong file todos.js, ta sẽ định nghĩa reducer như sau:
const todosReducer = (state = initialState, action) => {
switch (action.type) {
case 'GET_TODOS_REQUEST':
return {
...state,
loading: true,
error: null,
};
case 'GET_TODOS_SUCCESS':
return {
...state,
list: action.payload,
loading: false,
error: null,
};
case 'GET_TODOS_FAILURE':
return {
...state,
loading: false,
error: action.payload,
};
default:
return state;
}
};
Ở đây, ta định nghĩa reducer cho todo với các action type như GET_TODOS_REQUEST, GET_TODOS_SUCCESS, và GET_TODOS_FAILURE. Các action này tương ứng với việc lấy danh sách todo từ server với request, response thành công và response thất bại. Khi nhận được action, reducer sẽ trả về một state mới tương ứng với action đó.
Cuối cùng, ta sẽ kết hợp reducer này với rootReducer. Trong file reducers/index.js, ta sẽ import reducer todosReducer và thêm nó vào rootReducer như sau:
import { combineReducers } from 'redux';
import authReducer from './auth';
import todosReducer from './todos';
const rootReducer = combineReducers({
auth: authReducer,
todos: todosReducer,
});
export default rootReducer;
Giờ đây, ta đã tạo xong reducer cho todo và đã kết hợp nó vào rootReducer của Redux.
Cấu trúc thư mục dự án của chúng ta sẽ tương tự như dưới đây:
next-js-course-101/
├── pages/
│ ├── api/
│ │ ├── authenticate.js
│ │ └── todo.js
│ ├── index.js
│ ├── login.js
│ └── todo.js
├── components/
│ ├── Layout.js
│ └── TodoItem.js
├── redux/
│ ├── actions/
│ │ ├── actionTypes.js
│ │ ├── authActions.js
│ │ └── todoActions.js
│ ├── reducers/
│ │ ├── authReducer.js
│ │ └── todoReducer.js
│ └── store.js
├── styles/
│ ├── global.css
│ └── Home.module.css
├── .env.local
├── .gitignore
├── next.config.js
├── package.json
└── README.md
Lưu ý: Cấu trúc thư mục và file có thể khác nhau tùy thuộc vào cách bạn tổ chức dự án của mình.
Trong bài tiếp theo, chúng ta sẽ tạo một layout component trong Next.js với Bootstrap 5 để giúp cho ứng dụng của chúng ta có giao diện trực quan hơn.
Bài tiếp theo: Next.js bài 4: Tạo một thành phần bố cục (layout) trong Next.js với Bootstrap 5
