React State Management Libraries: Không chỉ có Redux 👌 (Tập 1)

Một vấn đề muôn thuở trong quá trình phát triển sản phẩm mà các lập trình viên React luôn gặp phải đó là làm sao để quản lý state một cách tối ưu và hiệu quả nhất. Trong React, có rất nhiều cách để tiếp cận với việc quản lý state mà mình sẽ kể đến trong bài viết này.

Nói sơ qua Redux thì Redux quá nổi tiếng trong cộng đồng React rồi. Chỉ cần nhìn qua lượng sao mà Redux nhận được trên Github thì cũng hiểu nó khủng đến mức nào.

Lượng sao của Redux trên Github

Vì Redux và React giống như một cặp bài trùng nên hầu hết mọi người cứ áp dụng Redux vào bất kì trường hợp nào dù to hay nhỏ. Vậy nên, mình sẽ giới thiệu tới các bạn một số điểm thú vị của các thư viện trong việc quản lý trạng thái (state management) trong React ngoài Redux ra nhé!


Akita 🐺 (> 3k⭐)

Github: https://github.com/datorama/akita

Dù đó có là Angular, React, Vue hay Javascript thuần thì Akita vẫn có thể đảm nhiệm được trọng trách là một công cụ hữu hiệu cho việc duy trì, mở rộng các ứng dụng.

Các đặc điểm chính và thế mạnh của Akita mà mình tham khảo từ chính chủ Akita gồm

  • Akita là một kiểu mẫu quản lý trạng thái, được xây dựng dựa trên RxJS và lấy ý tưởng của nhiều kho chứa dữ liệu từ Flux và các cập nhật "bất khả xâm phạm" từ Redux cùng với khái niệm của "streaming data" để tạo ra Observable Data Store.
  • Akita ưu tiên sự tối giản. Nó giảm đi tối thiểu lượng thời gian để xây dựng các nền móng trước khi vào công đoạn chính.
  • Một thế mạnh khác của Akita là phù hợp cho cả lập trình viên có kinh nghiệm và chưa có kinh nghiệm. Do đó, Akita rất "thân thiện" với nhà phát triển.
  • Akita được dựa trên các nguyên tắc thiết kế hướng đối tượng thay vì lập trình hàm (functional programming), vì vậy các lập trình viên hướng đối tượng sẽ dễ dàng để tiếp cận hơn.
  • Akita có một kiểu mẫu cố định nên trong một team sẽ khó bắt gặp việc khác biệt trong thiết kế.

Trong kiến trúc của Akita gồm các phần chính như Store, Query, Model và Service theo như sơ đồ

Sơ đồ kiến trúc của Akita

Giải thích sơ qua về kiến trúc này thì:

  • Store: Đóng vai trò là nơi lưu trữ trạng thái của Store và các trạng thái này chỉ có thể bị thay đổi bởi Service
  • Service: Cũng như trong các design pattern phổ biến như MVC, Service sẽ mang trọng trách chứa các phương thức logic và hàm cập nhật. Ví dụ như các hàm về CRUD sẽ được chứa trong Service.
  • Query: Đóng vai trò là lớp trung gian chứa các phương thức cập nhật để Component sử dụng nhầm cập nhật hoặc thay đổi trạng thái của Store.Các Component sẽ không lấy trạng thái hoặc cập nhật trạng thái trực tiếp trên Store mà sẽ thông qua lớp Query.

Từ các đặc điểm trên thì có thể thấy Akita khá là dễ tiếp cận và phù hợp cho nhiều đối tượng lập trình viên. Về hệ sinh thái thì Akita cũng hỗ trợ không chỉ React mà cả Angular và Vue.


Unstated (> 7.7k⭐)

Github: https://github.com/jamiebuilds/unstated

Nếu với Akita, các lập trình viên đã được tiếp cận với một State Management tối giản những vẫn đáp ứng được các nhu cầu về tách biệt phần Logic và phần UI. Thì với Unstated, bỏ qua hết các yếu tốt về kiến trúc cầu kì đó là sự cực kì cực kì cực kì...tối giản và đơn giản để tiếp cận. Và nếu so sánh với Akita về mặt phổ biến trong cộng đồng, rõ ràng Unstated được ưa chuộng hơn nhiều.

Để thấy được Unstated đơn giản đến thế nào thì các bạn hãy xem qua ví dụ này

Một ví dụ được lấy trên Github của Unstated

Oke nếu sau ví dụ trên các bạn vẫn chưa bị thuyết phục bởi sự đơn giản của Unstated thì mình sẽ giải thích sơ qua về kiến trúc của thư viện này và sự hữu dụng của nó nhé.

  • Provider: Nếu bạn đã từng làm việc qua với React Context API thì Provider trong Unstated cũng giống với Provider của Context API. Các Component được bọc bởi Provider sẽ có ảnh hưởng đến các trạng thái trong Container. Ngoài ra, khi sử dụng Provider, chúng ta có thể dùng Dependency Injection để thiết lập các trạng thái nội bộ.
  • Container: Đóng vai trò là phần quan trọng nhất để kiến trúc thư viện Unstated. Container sẽ chứa các logic và phương thức chính để cập nhật trạng thái được khai báo. Nếu để ý thì hàm cập nhật trạng thái trong Container không khác gì một hàm setState() trong các React Class Component thông thường.
  • Subscribe: Sau khi đã có mảnh ghép quản lý các logic chính như Container, chúng ta sẽ cần một phần đóng vai trò để thông báo đến các Component con về trạng thái được cập nhật và sử dụng các phương thức cập nhật được khai báo trong Container.

Jotai (>3.7k ⭐)

Github: https://github.com/pmndrs/jotai

Facts: Jotai được đánh vần là "joe-tie", trong tiếng Nhật từ này có nghĩa là "state".

Thư viện để quản lý trang thái đơn giản hơn cả từ đầu bài đến giờ - Jotai. Đúng, các bạn không có đọc sai đâu, Jotai đơn giản đến mức mà mình không biết phải nói gì về cái thư viện này luôn. Vỏn vẹn duy nhất kiến trúc của Jotai chỉ có mỗi Atom, Atom, Atom và Atom.

Nói vậy thôi chứ Jotai sẽ gồm 3 phần chính mà mình sẽ chia như sau:

  • Primitive Atom: Các state sẽ được khai báo dưới dạng là một atom
const count = atom(0);
  • Derived Atom: Các atom này đóng vai trò là getter của một Primitive Atom nhất định do nhà phát triển thiết kế.
const getCount = atom((get) => get(count));
  • Writable Atom: Ngược lại với Derived Atom, Atom này sẽ đóng vai trò là setter của Primitive Atom. Gồm 2 argument bắt buộc là get, set và các argument không bắt buộc sẽ do chúng ta khai báo.
const multiplyCountAtom = atom(null, (get, set, by) => set(count, get(count) * by))

Zustand 🐻 (> 7.8k ⭐)

Zustand là giải pháp nhỏ, gọn, nhanh và dễ dàng mở rộng cho vấn đề quản lý trạng thái theo một các rất "gấu 🐻" đời. Thư viện này có API dễ dùng dựa trên các hooks, không hề gập khuôn theo một kiểu mẫu nhất định.

Mặc dù các bạn thấy trông Zustand nhìn thì có vẻ rất đáng yêu, tuy nhiên, móng vuốt gấu lúc nào cũng bén hơn bạn tưởng. Zustand có thể nói là trình quản lý trạng thái duy nhất giải quyết được các vấn đề nan giải như zombie child problem, react concurrencycontext loss.

Để sử dụng Zustand cho dự án, chúng ta sẽ cần kiến trúc:

  • Store: Đóng vai trò là một hook, bạn có thể gần như tất cả mọi thứ vào store, từ các biến thuần, các object và thậm chí là cả hàm. Tùy vào cách bạn cho vào store sẽ quyết định các hàm mà bạn có để sử dụng.
import create from 'zustand'

const useStore = create(set => ({
  bears: 0,
  increasePopulation: () => set(state => ({ bears: state.bears + 1 })),
  removeAllBears: () => set({ bears: 0 })
}))
Zustand's store

Và...đó là những gì bạn cần để sử dụng Zustand cho quản lý state. Đúng vậy! Chỉ cần mỗi store là hoàn tất mọi thứ. Sau đó thì các Component chỉ việc sử dụng hook useStore() của Zustand để gọi đến store nhất định. Đây là một lợi thế của Zustand khi không cần phải bọc các Component với Provider.


Đến đây thì bài viết cũng khá là dài rồi, mình sẽ chia bài này thành 2 phần bởi sẽ còn nhiều thư viện quản lý state rất thú vị trong hệ sinh thái của React mà mình rất muốn chia sẻ. Byeee!!! 🙋‍♂️

Góc Của Chung

Góc Của Chung