+10

Sau 2 năm, Redux đã thay đổi quá nhiều :)

Bắt đầu học React vào khoảng 2 năm trước, tất cả cũng khá ổn cho đến khi mình học thêm Redux 😅 Thực sự lúc ý mình khá mông lung về kiến trúc cũng như cách sử dụng của Redux, nó có vẻ rất phức tạp với một đứa cũng mới học React như mình. Đã không biết bao nhiêu lần mình đọc series Redux cho người mới bắt đầu của anh @phamvanduc mỗi lần quên kiến thức, hồi ý Redux cũng có khá lằng nhằng với những connect, mapStateToProps, mapDispatchToProps .... thực sự mình ko hiểu chúng được bao nhiêu.

Bẵng đi 2 năm, React đã sử dụng React Hooks gọn nhẹ hơn class Component, Redux cũng cải tiến thêm với redux-toolkit. Woa, khi mình tìm hiểu thì thấy rất dễ hiểu cũng như gọn nhẹ khi phát triển sản phẩm, ước gì 2 năm trước nó đã được như thế này, đỡ vất vả cho anh em.

1. Kiến trúc và luồng hoạt động

Trước mắt chúng ta nhắc lại khái niệm về Redux một chút, Redux là 1 thư viện giúp để quản lý và cập nhật trạng thái (state) của ứng dụng. Redux có thể được dùng với nhiều thư viện khác nhau nhưng React-Redux có lẽ là "cặp bài trùng" phổ biến nhất.

Redux store là nơi lưu trữ các state của ứng dụng, các reducer trong store có nhiệm vụ cập nhật state theo logic định nghĩa trước, tùy thuộc vào actions được kích hoạt ở UI.

  • actions sẽ là các javascript object
  • reducers ko chứa các logic bất đồng bộ, reducers cũng sẽ chỉnh sửa state trên bản sao khi thực thi để phòng tránh lỗi khi đang thực thi có thể làm mất mát state.

Middleware sẽ được dùng khi chúng ta muốn thực hiện các lời gọi bất đồng bộ trước khi cập nhật state. Hiện tại Redux có hỗ trợ middleware mặc định là Thunk, ko như phiên bản cách đây tầm 2 năm mình dùng, khi muốn thêm Thunk thì phải install package.

Về cơ bản thì kiến trúc và luồng hoạt động của Redux vẫn như vậy, không có gì thay đổi suốt vài năm qua. Phần tiếp chúng ta cùng tìm hiểu 1 công cụ mới được giới thiệu cách đây chưa lâu, đó là Redux-Toolkit

2. Redux Toolkit

Redux Toolkit đơn giản chỉ là 1 thư viện giúp triển khai code logic Redux một cách đơn giản, gọn gàng và tối ưu hơn. Với Redux Toolkit, developer sẽ cảm thấy "nhẹ người" hơn so với việc code Redux logic trước kia.

Cài đặt

npm install @reduxjs/toolkit

Dưới đây chúng ta sẽ cùng nêu ra các ưu điểm của việc dùng Redux Toolkit so với triển khai logic theo thông thường

Store Setup

Logic thông thường

// src/rootReducer.js
import { combineReducers } from 'redux'

import todosReducer from './features/todos/todosSlice'
import filtersReducer from './features/filters/filtersSlice'

const rootReducer = combineReducers({
  // Define a top-level state field named `todos`, handled by `todosReducer`
  todos: todosReducer,
  filters: filtersReducer
})

export default rootReducer
// src/store.js
import { createStore, applyMiddleware } from 'redux'
import thunkMiddleware from 'redux-thunk'
import { composeWithDevTools } from 'redux-devtools-extension'
import rootReducer from './reducer'

const composedEnhancer = composeWithDevTools(applyMiddleware(thunkMiddleware))

const store = createStore(rootReducer, composedEnhancer)
export default store

Với cách tổ chức logic như trên, chúng ta sẽ phải thực hiện các công việc sau:

  • Ghép các reducers con lại với nhau thành root reducer
  • Import root reducer vào store file
  • Nếu muốn sử dụng middleware thì cần phải cài đặt package, sử dụng hàm applyMiddleware để sử dụng

Redux Toolkit

// src/store.js
import { configureStore } from '@reduxjs/toolkit'

import todosReducer from './features/todos/todosSlice'
import filtersReducer from './features/filters/filtersSlice'

const store = configureStore({
  reducer: {
    // Define a top-level state field named `todos`, handled by `todosReducer`
    todos: todosReducer,
    filters: filtersReducer
  }
})

export default store

Redux Toolkit có API configureStore giúp đơn giản hóa quá trình thiết lập store. configStore wraps API createStore và tự động xử lý hầu hết các thiết lập store thay vì làm thủ công như khi ko dùng redux-toolkit

Với configureStore, chúng ta có được những lợi ích sau:

  • Tự động combine các reducer con thành root reducer
  • Tự động add middleware thunk
  • Config trong đúng 1 file, ko cần chia ra 2 file khá lằng nhằng

Slices

import { createSlice } from '@reduxjs/toolkit'

const initialState = []

const todosSlice = createSlice({
  name: 'todos',
  initialState,
  reducers: {
    todoAdded(state, action) {
      // ✅ This "mutating" code is okay inside of createSlice!
      state.push(action.payload)
    },
    todoToggled(state, action) {
      const todo = state.find(todo => todo.id === action.payload)
      todo.completed = !todo.completed
    },
    todosLoading(state, action) {
      return {
        ...state,
        status: 'loading'
      }
    }
  }
})

export const { todoAdded, todoToggled, todosLoading } = todosSlice.actions

export default todosSlice.reducer

Trước kia khi chưa có redux-toolkit, các actions sẽ được ở 1 nơi, các reducers sẽ ở 1 nơi khác. Khi có thay đổi gì thì phải sửa 2 nơi, khá là cồng kềnh. Với createSlice, actions và reducer được cùng chung 1 nhà, việc xử lý thay đổi tiện hơn biết bao nhiêu 😀

Xử lý logic thay đổi state trong reducer

Thông thường

function handwrittenReducer(state, action) {
  return {
    ...state,
    first: {
      ...state.first,
      second: {
        ...state.first.second,
        [action.someId]: {
          ...state.first.second[action.someId],
          fourth: action.someValue
        }
      }
    }
  }
}

Reducer sẽ thay đổi trên bản sao của state khi actions được dispatch đến, nếu state ở dạng phức tạp, lồng nhau cách kiểu thì code sẽ trông rất phức tạp 😕

Redux Toolkit

Reducer sẽ ko bao giờ được sửa trực tiếp trên state mà chỉ có thể làm việc đó trên bản sao của state, redux-toolkit với việc sử dụng thư viện Immer giúp chúng ta viết code như thay đổi trực tiếp state nhưng thực ra vẫn là thay đổi trên bản sao (trông sẽ gọn hơn rất nhiều). Xịn xò chưa =))

Lưu ý: Chúng ta chỉ có thể dùng kiểu này trong phạm vi createSlice mà thôi

function reducerWithImmer(state, action) {
  state.first.second[action.someId].fourth = action.someValue
}

Tài liệu tham khảo

https://redux.js.org/


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í