Chào mừng đến với Cafedev! Hôm nay chúng ta sẽ cùng nhau tìm hiểu về Redux và Redux Toolkit – hai công cụ mạnh mẽ dành cho việc quản lý trạng thái trong ứng dụng JavaScript. Redux là một bộ lưu trữ trạng thái dựa trên dự đoán, giúp việc quản lý, gỡ lỗi và kiểm tra ứng dụng trở nên dễ dàng hơn bao giờ hết. Cùng với Redux Toolkit, chúng ta có thể giảm bớt mã boilerplate và giải quyết các nhiệm vụ phổ biến một cách đơn giản. Hãy cùng khám phá chi tiết trong bài viết dưới đây!”

Redux là một bộ lưu trữ trạng thái có thể dự đoán cho các ứng dụng JavaScript, không riêng cho React. Nó giúp bạn viết các ứng dụng hoạt động một cách nhất quán trên các môi trường client, server và native. Bằng cách tập trung trạng thái của ứng dụng vào một nơi duy nhất, Redux giúp việc quản lý, gỡ lỗi và kiểm thử ứng dụng trở nên dễ dàng hơn.

1. Tại sao Redux?

  • Dự đoán được: Với Redux, trạng thái của ứng dụng được lưu trữ ở một nơi duy nhất, đó là store. Điều này làm cho việc theo dõi các thay đổi và gỡ lỗi trở nên dễ dàng hơn. Dễ bảo trì: Có một cấu trúc trạng thái dự đoán được và các mẫu rõ ràng để cập nhật trạng thái đó có thể làm cho mã của bạn dễ bảo trì hơn. Render trên máy chủ: Redux hoạt động mượt mà với việc render trên phía máy chủ, cho phép bạn tải trước trạng thái, thực thi các hành động Redux trên máy chủ và gửi trạng thái xuống cho máy khách.

1.1 Store của Redux

Store của Redux là một bộ chứa chứa trạng thái của ứng dụng của bạn. Nó được tạo ra bằng cách sử dụng hàm createStore từ thư viện Redux. Store có một số trách nhiệm sau:

  • Giữ trạng thái của ứng dụng
  • Cho phép truy cập vào trạng thái thông qua getState()
  • Cho phép cập nhật trạng thái thông qua dispatch(action)
  • Đăng ký người nghe thông qua subscribe(listener)


Dưới đây là cách bạn tạo một store Redux:

import { createStore } from 'redux';

// Reducer function
function counterReducer(state = 0, action) {
switch (action.type) {
case 'INCREMENT':
return state + 1;
case 'DECREMENT':
return state - 1;
default:
return state;
}
}

const store = createStore(counterReducer);

Đoạn mã này tạo ra một store với một counterReducer đơn giản. Reducer lắng nghe các hành động INCREMENDECREMENT để điều chỉnh trạng thái.

1.2 Hành động (Actions)

Hành động là các đối tượng JavaScript đơn giản đại diện cho ý định thay đổi trạng thái. Mỗi hành động phải có một thuộc tính type để chỉ định loại hành động đang được thực hiện. Hành động có thể tùy chọn có một payload chứa thông tin cần thiết để thực thi hành động.

const incrementAction = {
  type: 'INCREMENT'
};

const decrementAction = {
  type: 'DECREMENT'
};

1.3 Reducer

Reducers là các hàm thuần túy nhận trạng thái hiện tại và một hành động làm đối số và trả về một trạng thái mới. Vai trò của reducer là chỉ định cách trạng thái thay đổi phản hồi vào một hành động.

function counterReducer(state = 0, action) {
  switch (action.type) {
    case 'INCREMENT':
      return state + 1;
    case 'DECREMENT':
      return state - 1;
    default:
      return state;
  }
}

2. Kết nối Redux với ứng dụng React

Để kết nối Redux với ứng dụng React, bạn thường sử dụng thư viện react-redux. Thư viện này cung cấp một thành phần Provider làm cho store Redux có sẵn cho phần còn lại của ứng dụng của bạn.

import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { createStore } from 'redux';
import App from './App'; // Your main App component
import rootReducer from './reducers'; // Your combined reducers

const store = createStore(rootReducer);

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
);

3. Redux với Redux Toolkit

Redux Toolkit (RTK) đơn giản hóa việc phát triển ứng dụng Redux bằng cách cung cấp một bộ công cụ để giảm mã boilerplate và đơn giản hóa các nhiệm vụ phổ biến.

3.1 Tạo một Slice với Redux Toolkit

Một “slice” là một bộ logic reducer Redux và hành động cho một tính năng duy nhất của ứng dụng của bạn. Hàm createSlice của RTK đơn giản hóa quá trình viết reducer và tạo ra hành động.

import { createSlice } from '@reduxjs/toolkit';

const counterSlice = createSlice({
  name: 'counter',
  initialState: 0,
  reducers: {
    increment: state => state + 1,
    decrement: state => state - 1,
  },
});

export const { increment, decrement } = counterSlice.actions;
export default counterSlice.reducer;

Trong ví dụ này, createSlice tự động tạo ra các tác nhân hành động cho mỗi hàm reducer mà chúng ta định nghĩa.

4. Redux Nâng cao: Vượt ra ngoài cơ bản

Khi bạn cảm thấy thoải mái hơn với các khái niệm cơ bản của Redux, đến lúc khám phá các tính năng và mẫu nâng cao của nó. Những điều này có thể giúp bạn giải quyết các thách thức quản lý trạng thái phức tạp, tối ưu hóa hiệu suất và duy trì một cơ sở mã có thể mở rộng và dễ bảo trì.

5. Các Mẫu và Kỹ Thuật Nâng Cao

5.1 Selector và Chuẩn hóa Trạng thái

Selector là các hàm lấy các phần của trạng thái cho các thành phần của bạn. Sử dụng selectors có thể tối ưu hóa truy cập và tính toán trạng thái, đặc biệt là với thư viện reselect, làm cho việc lựa chọn tối ưu hóa được lưu trữ để tăng hiệu suất.
Chuẩn hóa hình dạng trạng thái là rất quan trọng trong các ứng dụng phức tạp để đảm bảo dữ liệu được lưu trữ trong một cấu trúc phẳng, dự đoán được, làm cho việc cập nhật và truy xuất dễ dàng hơn. Cách tiếp cận này giảm thiểu sự trùng lặp và tạo điều kiện cho các mẫu truy xuất đơn giản hơn.

import { createSelector } from 'reselect';

const selectNormalizedData = state => state.data.entities;
const selectIds = state => state.data.ids;

const selectDataArray = createSelector(
  [selectNormalizedData, selectIds],
  (entities, ids) => ids.map(id => entities[id])
);

5.2 Middleware Thunk cho Logic Bất Đồng Bộ

Redux Thunk là một middleware cho phép bạn viết các action creator trả về một hàm thay vì một action. Điều này đặc biệt hữu ích cho việc xử lý các hoạt động bất đồng bộ trong các action, như các cuộc gọi API.

function fetchData() {
return async dispatch => {
dispatch({ type: 'FETCH_START' });
try {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
dispatch({ type: 'FETCH_SUCCESS', payload: data });
} catch (error) {
dispatch({ type: 'FETCH_ERROR', error });
}
};
}

5.3 Middleware Tùy Biến

Việc tạo middleware tùy chỉnh có thể cung cấp các giải pháp cá nhân hóa để chặn các action hoặc hoạt động bất đồng bộ, ghi thông tin nhật ký, gửi phân tích, hoặc triển khai logic kinh doanh tùy chỉnh.

const customMiddleware = store => next => action => {
// Your custom logic here
console.log('Action dispatched:', action);
return next(action);
};

6. Tối Ưu Hóa Hiệu Suất

Quản lý hiệu suất là rất quan trọng trong các ứng dụng quy mô lớn. Redux cung cấp một số chiến lược để giúp giữ cho ứng dụng của bạn hoạt động mượt mà.

  • Phân Nhóm Dispatch : Nhóm nhiều cập nhật trạng thái thành một lần kích hoạt lại có thể giảm đáng kể tác động về hiệu suất của việc phát nhiều action liên tiếp.
  • Memoization : Ngăn chặn việc tính toán và kích hoạt lại không cần thiết bằng cách memoize các thành phần và hàm chọn lọc.
  • Tải Trễ : Chia nhỏ Redux store và logic của bạn bằng các kỹ thuật phân chia mã có thể cải thiện thời gian tải ban đầu của ứng dụng.

7. Mô Hình Tích Hợp

Sự linh hoạt của Redux cho phép nó được tích hợp với các framework và thư viện khác ngoài React, như Angular hoặc Vue. Chìa khóa là kết nối Redux store với vòng đời thành phần của framework, đảm bảo rằng các thay đổi trạng thái kích hoạt cập nhật UI một cách phù hợp.

7.1 Redux với Angular

Tích hợp Redux với Angular bao gồm việc sử dụng gói ng-redux để cung cấp một cơ chế kết nối tương tự như React-Redux, cho phép các thành phần đăng ký theo dõi cập nhật store và phát các action.

7.2 Redux với Vue

Hệ thống phản ứng của Vue có thể được tăng cường bằng Redux cho quản lý trạng thái toàn cầu bằng cách sử dụng hệ thống plugin của Vue để tiêm Redux store vào tất cả các thành phần, cho phép chúng phát các action và đăng ký theo dõi cập nhật store.

8. Sử Dụng Redux Toolkit Nâng Cao

Redux Toolkit giúp đơn giản hóa quá trình phát triển Redux, nhưng cũng hỗ trợ các trường hợp sử dụng nâng cao:

  • Tạo Slice Tùy Chỉnh : Các kỹ thuật chia tách tiên tiến có thể giúp quản lý logic trạng thái phức tạp hiệu quả hơn. Phân Chia Mã và Tải Trễ Reducers : RTK hỗ trợ tải động reducers cho các ứng dụng được phân chia mã, cải thiện thời gian tải và khả năng mở rộng.
  • Cải Tiến Middleware : RTK cho phép middleware tùy chỉnh, cải thiện ngăn xếp middleware Redux mặc định bằng các chức năng bổ sung như ghi nhật ký, báo cáo sự cố, hoặc quản lý nhiệm vụ bất đồng bộ.

9. Sử Dụng Redux DevTools để Gỡ Lỗi

Redux DevTools là một tiện ích mở rộng cho Chrome và Firefox cung cấp các công cụ mạnh mẽ để giám sát và thay đổi trạng thái của các ứng dụng Redux. Bạn có thể tích hợp Redux DevTools với ứng dụng của bạn để quan sát các hành động và thay đổi trạng thái trong thời gian thực, “du hành thời gian” bằng cách quay trở lại các trạng thái trước đó, và nhiều hơn nữa.

10. Kịch Bản Thực Tế (Ví dụ)

10.1 Kịch Bản 1: Quản lý Phiên Người Dùng trong Ứng Dụng Mạng Xã Hội

Vấn Đề : Một ứng dụng mạng xã hội cần xử lý xác thực người dùng, quản lý trạng thái phiên (đăng nhập/đăng xuất), và cá nhân hóa trải nghiệm người dùng dựa trên hồ sơ và hoạt động của họ.

Giải Pháp với Redux : Ứng dụng có thể sử dụng Redux để quản lý trạng thái toàn cục liên quan đến phiên người dùng, trạng thái xác thực, và hồ sơ người dùng. Bằng cách phát các hành động như LOGIN_SUCCESS , LOGOUT , và FETCH_PROFILE_SUCCESS , ứng dụng có thể cập nhật trạng thái phản ứng với tương tác của người dùng và dữ liệu lấy từ API. Redux Thunk hoặc Redux Saga có thể xử lý các hành động bất đồng bộ, như lấy dữ liệu người dùng sau khi đăng nhập, đảm bảo trải nghiệm người dùng mượt mà và phản hồi.

// Example of an async action creator for fetching user profile
const fetchUserProfile = userId => async dispatch => {
dispatch({ type: 'FETCH_PROFILE_START' });
try {
const response = await fetch(`/api/users/${userId}`);
const profile = await response.json();
dispatch({ type: 'FETCH_PROFILE_SUCCESS', payload: profile });
} catch (error) {
dispatch({ type: 'FETCH_PROFILE_FAILURE', error });
}
};

10.2 Kịch Bản 2: Cập Nhật Thời Gian Thực trong Ứng Dụng Bảng Điều Khiển

Vấn Đề : Một bảng điều khiển phân tích cần hiển thị dữ liệu thời gian thực, bao gồm các số liệu người dùng, trạng thái hệ thống, và các chỉ số hiệu suất. Dữ liệu là động và được cập nhật thường xuyên.

Giải Pháp với Redux : Bảng điều khiển có thể tận dụng Redux để quản lý trạng thái dữ liệu của mình, cập nhật giao diện người dùng trong thời gian thực khi có dữ liệu mới đến. Middleware tùy chỉnh có thể lắng nghe các hành động cụ thể được kích hoạt bởi sự kiện WebSocket hoặc cơ chế đánh giá, cập nhật trạng thái tương ứng. Phương pháp này đảm bảo giao diện người dùng luôn phản ứng và cập nhật với dữ liệu mới nhất mà không cần làm mới trang hoàn toàn.

// Example of middleware to handle real-time data updates
const realTimeDataMiddleware = store => next => action => {
if (action.type === 'RECEIVE_REAL_TIME_DATA') {
// Process and update state with the new real-time data
const updatedData = processRealTimeData(action.payload);
store.dispatch({ type: 'UPDATE_DATA', payload: updatedData });
}
return next(action);
};

10.3 Kịch Bản 3: Logic Trạng Thái Phức Tạp trong Các Nền Tảng Thương Mại Điện Tử

Vấn Đề : Một nền tảng thương mại điện tử yêu cầu quản lý trạng thái phức tạp để xử lý giỏ hàng, tồn kho sản phẩm, đánh giá của người dùng, và quy trình thanh toán, bao gồm các chương trình khuyến mãi và giao dịch thanh toán.
Giải Pháp với Redux : createSlicecreateAsyncThunk của Redux Toolkit có thể giúp đơn giản hóa việc quản lý và cập nhật trạng thái phức tạp liên quan đến hoạt động thương mại điện tử. Ví dụ, quản lý giỏ hàng có thể hưởng lợi từ cấu trúc trạng thái được chuẩn hóa để cập nhật số lượng hoặc xóa các mục một cách hiệu quả. Thunk bất đồng bộ có thể quản lý cuộc gọi API để kiểm tra tồn kho hoặc gửi đơn hàng, đảm bảo trạng thái giao diện người dùng luôn phản ánh dữ liệu phía máy chủ mới nhất.

// Example of a slice for shopping cart management
const cartSlice = createSlice({
name: 'cart',
initialState: { items: {}, total: 0 },
reducers: {
addItem: (state, action) => {
const { productId, quantity } = action.payload;
if (state.items[productId]) {
state.items[productId].quantity += quantity;
} else {
state.items[productId] = { quantity };
}
// Update total cost, etc.
},
removeItem: (state, action) => {
delete state.items[action.payload.productId];
// Recalculate total, etc.
},
},
// Additional thunks and logic for API interactions
});

11. Kết Luận

Redux và Redux Toolkit cung cấp một phương pháp cấu trúc để quản lý trạng thái trong các ứng dụng JavaScript, giúp việc phát triển, bảo trì và gỡ lỗi các ứng dụng phức tạp trở nên dễ dàng hơn. Bằng cách hiểu rõ các khái niệm cốt lõi của store, actions, và reducers, và tận dụng các công cụ được cung cấp bởi Redux Toolkit, nhà phát triển có thể hiệu quả triển khai và quản lý trạng thái trong dự án của họ.
Hướng dẫn toàn diện này nhằm mục đích làm sâu rộng hiểu biết của bạn về Redux và hệ sinh thái của nó. Với những khái niệm và ví dụ này, bạn sẽ cảm thấy tự tin hơn khi làm việc với Redux và Redux Toolkit trong các dự án của mình. Hãy nhớ rằng, việc thành thạo Redux cần phải thực hành, vì vậy đừng ngần ngại thử nghiệm với những khái niệm này trong ứng dụng của bạn.

Trên đây là một cái nhìn tổng quan về Redux và Redux Toolkit – hai công cụ mạnh mẽ giúp quản lý trạng thái trong ứng dụng JavaScript. Để hiểu sâu hơn về cách sử dụng chúng và tận dụng tối đa tính linh hoạt của Redux, hãy tham khảo các tài nguyên và hướng dẫn hữu ích mà Cafedev đã chia sẻ. Chúng tôi hy vọng rằng thông qua bài viết này, bạn sẽ có thêm kiến thức và sự tự tin khi làm việc với Redux và Redux Toolkit trong dự án của mình. Hãy tiếp tục khám phá và áp dụng những điều bạn đã học được vào thực tế!”

Các nguồn kiến thức MIỄN PHÍ VÔ GIÁ từ cafedev tại đây

Nếu bạn thấy hay và hữu ích, bạn có thể tham gia các kênh sau của CafeDev để nhận được nhiều hơn nữa:

Chào thân ái và quyết thắng!

Đăng ký kênh youtube để ủng hộ Cafedev nha các bạn, Thanks you!