-1

Redux vs Angular 2

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 theo users với type là UsersState
  • ThreadsReducer sẽ hoạt động theo threads 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

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí