[React Basics] Redux Toolkit: Học cách nâng cấp Redux với các công cụ tiện lợi

Trong thế giới phát triển ứng dụng web hiện đại, đặc biệt là với các thư viện như React, việc quản lý state của ứng dụng là một trong những thách thức lớn nhất. Khi ứng dụng ngày càng lớn và phức tạp, state trở nên phân tán, khó theo dõi và càng khó để gỡ lỗi. Redux ra đời như một vị cứu tinh, mang đến một cấu trúc quản lý state tập trung và dễ đoán. Tuy nhiên, nhiều lập trình viên lại cảm thấy Redux "nguyên thủy" quá rườm rà, đòi hỏi nhiều boilerplate code (code lặp đi lặp lại) và khó để cấu hình.

Redux Toolkit: Học cách nâng cấp Redux với các công cụ tiện lợi

Chính lúc này, Redux Toolkit (RTK) xuất hiện và thay đổi cuộc chơi. Được chính đội ngũ Redux tạo ra, Redux Toolkit không phải là một thư viện mới, mà là một bộ công cụ "chính chủ", được thiết kế để đơn giản hóa và tối ưu hóa quá trình làm việc với Redux. Hãy cùng khám phá tại sao Redux Toolkit lại được coi là tiêu chuẩn cho việc quản lý state trong các ứng dụng React ngày nay. 🚀

Redux Toolkit là gì? Tại sao nó lại quan trọng?

Hãy tưởng tượng bạn đang xây một mô hình LEGO phức tạp. Redux "nguyên thủy" giống như việc bạn phải tự đi nhặt từng mảnh LEGO nhỏ riêng lẻ, tự đọc bản thiết kế chi tiết và lắp ráp chúng lại với nhau. Quá trình này rất mất thời gian và dễ xảy ra lỗi.

Redux Toolkit giống như một bộ LEGO đã được đóng gói sẵn. Nó không chỉ cung cấp cho bạn tất cả các mảnh cần thiết mà còn sắp xếp chúng một cách logic, đi kèm với hướng dẫn cực kỳ rõ ràng và các công cụ hỗ trợ giúp bạn xây dựng nhanh hơn, hiệu quả hơn và ít sai sót hơn.

Về bản chất, Redux Toolkit là một gói (@reduxjs/toolkit) bao gồm tất cả các API và tiện ích cần thiết để bạn có thể viết code Redux theo cách tốt nhất. Nó giải quyết triệt để các "nỗi đau" của lập trình viên khi dùng Redux:

  • Quá nhiều cấu hình: Việc thiết lập một Redux store yêu cầu nhiều bước và cài đặt nhiều thư viện (như redux-thunk, reselect, immer, redux-devtools-extension).
  • Quá nhiều boilerplate: Để tạo một action và một reducer đơn giản, bạn phải tạo nhiều file và viết rất nhiều dòng code lặp lại.
  • Tính bất biến (Immutability) phức tạp: Việc phải cập nhật state một cách bất biến (tức là tạo ra bản sao mới thay vì chỉnh sửa trực tiếp) dễ gây ra lỗi nếu không cẩn thận, đặc biệt với các state lồng nhau.

Redux Toolkit đã đơn giản hóa tất cả những điều này bằng cách tích hợp sẵn các thư viện tốt nhất và cung cấp các API mạnh mẽ, giúp bạn viết ít code hơn nhưng vẫn đạt được hiệu quả cao hơn.

Những "vũ khí" lợi hại của Redux Toolkit

Redux Toolkit trang bị cho bạn một loạt các công cụ mạnh mẽ. Dưới đây là những thành phần cốt lõi đã làm nên tên tuổi của nó.

1. configureStore() - Thiết lập Store chưa bao giờ dễ hơn

Thay vì phải tự tay kết hợp các reducer, thêm middleware, và cấu hình Redux DevTools, configureStore sẽ làm tất cả điều đó cho bạn chỉ với một hàm duy nhất.

  • Tự động kết hợp reducers: Bạn chỉ cần truyền vào một object chứa các reducer, configureStore sẽ tự động gọi combineReducers.
  • Tích hợp sẵn middleware mặc định: redux-thunk (để xử lý logic bất đồng bộ) và các middleware kiểm tra lỗi (như kiểm tra state có bị thay đổi trực tiếp không) được tự động thêm vào.
  • Tự động bật Redux DevTools Extension: Bạn không cần cài đặt thêm bất kỳ đoạn mã nào để sử dụng công cụ gỡ lỗi tuyệt vời này.

Ví dụ:

import { configureStore } from '@reduxjs/toolkit'
import userReducer from './features/userSlice'
import cartReducer from './features/cartSlice'

const store = configureStore({
  reducer: {
    user: userReducer,
    cart: cartReducer,
  },
})

export default store

Chỉ với vài dòng code, bạn đã có một Redux store hoàn chỉnh và được tối ưu hóa.

2. createSlice() - Tạm biệt boilerplate code

Đây có lẽ là tính năng đột phá và được yêu thích nhất của Redux Toolkit. createSlice cho phép bạn định nghĩa một phần (slice) của state, bao gồm reducer và các action liên quan, tất cả trong một file duy nhất.

  • Tự động tạo Action Creators: Bạn chỉ cần viết các hàm reducer, createSlice sẽ tự động tạo ra các action creator tương ứng với tên của hàm đó.
  • Sử dụng Immer bên trong: createSlice tích hợp thư viện Immer, cho phép bạn viết code "thay đổi trực tiếp" state trong reducer. Immer sẽ lo phần xử lý phức tạp để đảm bảo state vẫn được cập nhật một cách bất biến ở phía sau. Điều này giúp code của bạn ngắn gọn và dễ đọc hơn rất nhiều.

Ví dụ về một "slice":

import { createSlice } from '@reduxjs/toolkit'

const counterSlice = createSlice({
  name: 'counter',
  initialState: {
    value: 0,
  },
  reducers: {
    increment: (state) => {
      // Bạn có thể "thay đổi trực tiếp" state ở đây!
      state.value += 1
    },
    decrement: (state) => {
      state.value -= 1
    },
    incrementByAmount: (state, action) => {
      state.value += action.payload
    },
  },
})

// Tự động tạo ra action creators
export const { increment, decrement, incrementByAmount } = counterSlice.actions

// Xuất ra reducer
export default counterSlice.reducer

Với createSlice, bạn không còn phải viết các hằng số action type, các hàm action creator và các câu lệnh switch dài dòng trong reducer nữa. Mọi thứ đều được gói gọn một cách ngăn nắp.

3. createAsyncThunk() - Xử lý bất đồng bộ thanh lịch

Việc gọi API và xử lý các tác vụ bất đồng bộ là một phần không thể thiếu của ứng dụng web. createAsyncThunk là một API được thiết kế đặc biệt để xử lý các luồng bất đồng bộ (ví dụ: fetching data) một cách chuẩn hóa.

Nó sẽ tự động tạo ra các action type cho các trạng thái của một promise (pending, fulfilled, rejected) và bạn có thể lắng nghe các action này trong reducer của mình để cập nhật state tương ứng (ví dụ: hiển thị loading spinner, hiển thị dữ liệu khi thành công, hoặc hiển thị lỗi khi thất bại).

Ví dụ:

import { createAsyncThunk, createSlice } from '@reduxjs/toolkit'
import { fetchUserById } from './userAPI'

// Tạo một thunk bất đồng bộ
export const getUserById = createAsyncThunk(
  'users/fetchById',
  async (userId, thunkAPI) => {
    const response = await fetchUserById(userId)
    return response.data
  },
)

const userSlice = createSlice({
  name: 'users',
  initialState: { entities: [], loading: 'idle' },
  reducers: {},
  extraReducers: (builder) => {
    builder
      .addCase(getUserById.pending, (state) => {
        state.loading = 'loading'
      })
      .addCase(getUserById.fulfilled, (state, action) => {
        state.loading = 'idle'
        state.entities.push(action.payload)
      })
      .addCase(getUserById.rejected, (state) => {
        state.loading = 'failed'
      })
  },
})

export default userSlice.reducer

4. RTK Query - Data Fetching và Caching tuyệt vời

Nếu createAsyncThunk là một công cụ tuyệt vời cho các tác vụ bất đồng bộ, thì RTK Query còn đưa nó lên một tầm cao mới. Đây là một giải pháp fetching và caching dữ liệu mạnh mẽ, được xây dựng ngay trên Redux Toolkit. Nó được lấy cảm hứng từ các thư viện hiện đại như React Query hay SWR.

Với RTK Query, bạn chỉ cần định nghĩa các "endpoints" API của mình. Nó sẽ tự động xử lý toàn bộ quá trình:

  • Tự động fetching, re-fetching, và caching dữ liệu.
  • Tự động tạo ra các React hooks (useQuery, useMutation) để bạn sử dụng trong component một cách dễ dàng.
  • Quản lý trạng thái loading và error mà không cần bạn phải viết thêm code.
  • Tối ưu hóa hiệu năng bằng cách tránh các lần gọi API không cần thiết.

RTK Query là một công cụ cực kỳ mạnh mẽ, giúp đơn giản hóa phần lớn logic liên quan đến việc tương tác với server.

Bắt đầu với Redux Toolkit như thế nào?

Cách tốt nhất để bắt đầu là sử dụng template chính thức của Create React App:

npx create-react-app my-app --template redux

Hoặc nếu bạn muốn thêm vào một dự án có sẵn:

npm install @reduxjs/toolkit react-redux

Sau đó, hãy bắt đầu bằng việc tạo store với configureStore và định nghĩa các slice của state bằng createSlice. Cấu trúc thư mục được khuyến khích là đặt tất cả logic của một "feature" (ví dụ: user, product) vào cùng một thư mục (features/user/userSlice.js).

Kết luận: Tương lai của Redux chính là Redux Toolkit

Redux Toolkit không chỉ là một "phiên bản dễ dùng hơn" của Redux. Nó là kết quả của nhiều năm kinh nghiệm từ cộng đồng, đúc kết lại thành một bộ công cụ mạnh mẽ, hiệu quả và dễ tiếp cận. Nó giúp bạn viết code Redux nhanh hơn, gọn gàng hơn, và ít lỗi hơn.

Nếu bạn đang bắt đầu một dự án mới với Redux, hãy sử dụng Redux Toolkit. Nếu bạn đang làm việc trên một dự án Redux cũ, hãy cân nhắc việc chuyển đổi sang Redux Toolkit. Lợi ích mà nó mang lại về năng suất và khả năng bảo trì là không thể phủ nhận. Redux Toolkit thực sự là cách mà Redux được sinh ra để hoạt động. ✨

Chúc bạn viết code vui vẻ với với React Redux và Toolkit!

Bài viết liên quan

[React Basics] React Hook là gì? Hướng dẫn chi tiết cho người mới bắt đầu

Bạn đang tìm hiểu về React Hook? Bài viết này sẽ giải thích React Hook là gì, các loại Hook phổ biến và hướng dẫn từng bước cách áp dụng chúng vào dự án thực tế.

[React Basics] Hiểu rõ cách dùng useRef Hook trong React với các ví dụ thực tế

useRef hook là gì? Tìm hiểu cách sử dụng useRef trong React để tương tác với DOM, lưu trữ giá trị mà không gây re-render. Kèm theo ví dụ code chi tiết và dễ hiểu.

[React Basics] React Component Lifecycle: Tổng hợp những kiến thức bạn cần biết

React Component Lifecycle là một kiến thức nền tảng bạn phải biết. Tìm hiểu cách một component được tạo, cập nhật và xóa, từ đó tối ưu hiệu suất và tránh các lỗi thường gặp trong ứng dụng React.

[React Basics] React Handling Events: Những cách làm hiệu quả và tối ưu

Handling Events là một kỹ năng quan trọng trong React. Bài viết này sẽ giúp bạn hiểu rõ về cú pháp, cách truyền đối số và quản lý trạng thái khi làm việc với các sự kiện.