Redux vs Angular 2
Bài đăng này đã không được cập nhật trong 6 năm
Xin chào các bạn, bữa hôm có vụ nghiên cứu về tốc độ của mấy framework, ngồi so sánh thử tốc độ của react, angular 1, 2 như nào? Cái nào ngon hơn, nên dùng cái nào mà không nên dùng cái nào? Test thử vài cái thấy angular 2 chạy nhanh thậc, nhưng không biết khi kết hợp với redux trong dự án thì kết quả nó sẽ cho ra như thế nào. Bài viết hôm nay mình sẽ nói sơ qua về cách kết hợp giữa Redux và Angular 2.
Để hiểu rõ chúng ta cùng xem ví dụ như sau: Các thành phần chính:
- ChatNavBar: chứa số lượng tin nhắn chưa đọc
- ChatThreads: hiển thị danh sách đối tượng mà mình sẽ tạo hội thoại
- ChatWindow: hiển thị danh sách tin nhắn của cuộc hội thoại hiện tại
Models
Chúng ta sẽ cần 3 đối tượng chính:
- User: thông tin người tham gia hội thoại
- Message: nội dung tin nhắn
- Thread: lưu trữ tập nội dung tin nhắn của cuộc hội thoại
Reducers
Chúng ta sẽ sử dụng 2 reducers:
- UserReducer: xử lý thông tin về user hiện tại
- ThreadsReducer: xử lý tập các threads và messages của hội thoại
Implementing
Chúng ta sẽ lần lượt đi vào định nghĩa từng models
Models
User.ts
export interface User {
id: string;
name: string;
avatarSrc: string;
isClient?: boolean;
}
Biến isClient sẽ cho chúng ta biết người dùng có phải là người dùng đang sử dụng app hay không?
Thread.ts
export interface Thread {
id: string;
name: string;
avatarSrc: string;
messages: Message[];
}
Message.ts
export interface Message {
id?: string;
sentAt?: Date;
isRead?: boolean;
thread?: Thread;
author: User;
text: string;
}
Với mỗi message chúng ta sẽ có:
- id: id của message
- sentAt: thời gian send message
- isRead: biến boolean kiểm tra trang thái tin nhắn đã được đọc hay chưa
- author: user send message
- thread: tham chiếu tới Thread đang quản lý
App State
Chúng ta đã có đầy đủ model cần thiết, tuy nhiên chúng ta cũng cần 1 biến để lưu trữ trạng thái của app, nó sẽ cần cả users, và tập các thread:
export interface AppState {
users: UsersState;
threads: ThreadsState;
}
The Root Reducer
Chúng ta cùng xem root reducer dưới đây:
export interface AppState {
users: UsersState;
threads: ThreadsState;
}
const rootReducer: Reducer<AppState> = combineReducers<AppState>({
users: UsersReducer,
threads: ThreadsReducer
});
Có một vài điểm đáng chú ý ở đây:
UsersReduces
sẽ hoạt động theousers
với type làUsersState
ThreadsReducer
sẽ hoạt động theothreads
với type làThreadsState
UsersState
- UsersState sẽ tham chiếu tới currentUser.
UserReducer.ts
:
export interface UsersState {
currentUser: User;
};
const initialState: UsersState = {
currentUser: null
};
ThreadsReducer.ts
export interface ThreadsEntities {
[id: string]: Thread;
}
export interface ThreadsState {
ids: string[];
entities: ThreadsEntities;
currentThreadId?: string;
};
const initialState: ThreadsState = {
ids: [],
currentThreadId: null,
entities: {}
};
Chúng ta sẽ định nghĩa 1 interface là ThreadsEntities nó sẽ map với các thread ids của list Threads, với ý tưởng đưa ra là việc tìm kiếm các thread sẽ dễ dàng hơn Chúng ta cũng sẽ lưu thêm 1 biến là curentThreadId - thread hiện tại mà user đang trỏ vào
Building the Reducers (and Action Creators)
Về cá nhân mình thấy thì quản lý action là mục mình hứng thú nhất Chúng ta sẽ đi vào các action chính
Set Current User Action Creators
UsersState sẽ lưu thông tin current_user, chúng ta sẽ cần thêm các actions như sau:
actions/UserActions.ts
export const SET_CURRENT_USER = '[User] Set Current';
export interface SetCurrentUserAction extends Action {
user: User;
}
export const setCurrentUser: ActionCreator<SetCurrentUserAction> =
(user) => ({
type: SET_CURRENT_USER,
user: user
});
Chúng ta định nghĩa một constant SetCurrentUserAction
, chúng ta sẽ sử dụng để chuyển đổi trong reducer
Ngoài ra cũng định nghĩa themem subinterface SetCurrentUserAction được kế thừa từ action và thêm vào thuộc tính user
UsersReducer - Set Current User
Chúng ta cùng chú ý vào UsersReducer
:
export const UsersReducer =
function(state: UsersState = initialState, action: Action): UsersState {
switch (action.type) {
case UserActions.SET_CURRENT_USER:
const user: User = (<UserActions.SetCurrentUserAction>action).user;
return {
currentUser: user
};
default:
return state;
}
};
UsersReducer sẽ lấy 1 UsersState làm đối số đầu tiên (lưu ý là nó cũng không phải là AppState mà chỉ là 1 reducer con trên, 1 nhánh trên cây state)
UsersReducer cung giống với reducers, sẽ trả về một state mới, trong trường hợp này sẽ trả về theo UsersState.
Để set current user chúng ta cần nhận 1 user từ 1 action gửi đến. chúng ta sẽ hứng action UserActions.SetCurrentUserAction
và sau đó đọc thuộc tính user
Thread and Mesages
Nội dung chính của ứng dụng là việc gửi và nhận tin. Ở mục này chúng ta sẽ có 3 action chính cần support:
- thêm mới thread vào state
- thêm message vào 1 thread
- chọn thread Sau đây chúng ta cùng đi đến với việc tạo mới 1 thread
thêm mới thread trong action creators
Chúng ta cùng đến với actions/ThreadActions.ts
như bên dưới
export const ADD_THREAD = '[Thread] Add';
export interface AddThreadAction extends Action {
thread: Thread;
}
export const addThread: ActionCreator<AddThreadAction> =
(thread) => ({
type: ADD_THREAD,
thread: thread
})
Tượng tự như UserAction, ThreadAction sẽ định nghĩa một const ADD_THREAD để chuyển đổi action.
Thêm mới thread tron reducer
tiếp đến với file reducers/ThreadsReducer.ts
export const ThreadsReducer =
function(state: ThreadsState = initialState, action: Action): ThreadsState {
switch (action.type) {
// Adds a new Thread to the list of entities
case ThreadActions.ADD_THREAD: {
const thread = (<ThreadActions.AddThreadAction>action).thread;
if (state.ids.includes(thread.id)) {
return state;
}
return {
ids: [ ...state.ids, thread.id ],
currentThreadId: state.currentThreadId,
entities: Object.assign({}, state.entities, {
[thread.id]: thread
})
};
}
thêm mới message trong action creators
actions/ThreadActions.ts
export const ADD_MESSAGE = '[Thread] Add Message';
export interface AddMessageAction extends Action {
thread: Thread;
message: Message;
}
export const ADD_MESSAGE = '[Thread] Add Message';
export interface AddMessageAction extends Action {
thread: Thread;
message: Message;
}
Thêm mới message trong reducer
case ThreadActions.ADD_MESSAGE: {
const thread = (<ThreadActions.AddMessageAction>action).thread;
const message = (<ThreadActions.AddMessageAction>action).message;
// special case: if the message being added is in the current thread, then
// mark it as read
const isRead = message.thread.id === state.currentThreadId ?
true : message.isRead;
const newMessage = Object.assign({}, message, { isRead: isRead });
// grab the old thraed from entities
const oldThread = state.entities[thread.id];
// create a new thread which has our newMessage
const newThread = Object.assign({}, oldThread, {
messages: [...oldThread.messages, newMessage]
});
return {
ids: state.ids, // unchanged
currentThreadId: state.currentThreadId, // unchanged
entities: Object.assign({}, state.entities, {
[thread.id]: newThread
})
};
chọn current thread trong action creator
export const SELECT_THREAD = 'Thread Select';
export interface SelectThreadAction extends Action {
thread: Thread;
}
export const selectThread: ActionCreator<SelectThreadAction> =
(thread) => ({
type: SELECT_THREAD,
thread: thread
});
chọn current thread trong reducer
// Select a particular thread in the UI
case ThreadActions.SELECT_THREAD: {
const thread = (<ThreadActions.SelectThreadAction>action).thread;
const oldThread = state.entities[thread.id];
// mark the messages as read
const newMessages = oldThread.messages.map(
(message) => Object.assign({}, message, { isRead: true }));
// give them to this new thread
const newThread = Object.assign({}, oldThread, {
messages: newMessages
});
return {
ids: state.ids,
currentThreadId: thread.id,
entities: Object.assign({}, state.entities, {
[thread.id]: newThread
})
};
}
default:
return state;
}
};
Chúng ta sẽ bắt đầu với với thread-to-select và sử dụng thread.id đó. chúng ta cũng cần copy tất cả các tin nhắn cũ và set trạng tháng isRead = true và trả về 1 state mới.
Tổng kết
Trên đây mình đã trình bày về cấu trúc của ứng dụng chat, các thành phần liên quan đến redux như action creator, reducer. Trong phần tiếp theo mình sẽ trình bày việc xây dựng angular chat app để tạo thành 1 ứng dụng hoàn chỉnh Cảm ơn các bạn đã chú ý theo dõi Chi tiết các ban có thể xem thêm tại cuốn sách: ng-book 2, Felipe Coury, Ari Lerner, Nate Murray, & Carlos Taborda
All rights reserved