
Sophia Rodriguez
Đăng ngày 14 tháng 11, 2025

So sánh Zustand và Redux Toolkit: đơn giản vs cấu trúc, hiệu năng, boilerplate và mẫu thực tế để chọn công cụ tốt nhất cho ứng dụng React của bạn.
Năm 2025, quản lý state React không còn là độc quyền của Redux.
Zustand đã bùng nổ với cách tiếp cận hooks-first tối giản, trong khi Redux Toolkit (RTK) đã tiến hóa để loại bỏ tiếng xấu về boilerplate.
Cả hai đều immutable, hiệu năng cao và sẵn sàng production. Nhưng cái nào chiến thắng cho ứng dụng của bạn? Hãy cùng đào sâu vào code, mẫu và đánh đổi.
| Khía cạnh | Zustand | Redux Toolkit |
|---|---|---|
| Boilerplate | Tối thiểu (1 file) | Thấp (slices + store) |
| Kích thước Bundle | ~1.5KB | ~10KB |
| Độ khó học | Dễ (chỉ hooks) | Trung bình (actions/reducers) |
| DevTools | Cơ bản (tương thích Redux DevTools) | Nâng cao (time-travel, middleware) |
| Phù hợp nhất | App nhỏ-vừa, prototypes | Team lớn, logic phức tạp |
import { create } from 'zustand'; const useCounterStore = create((set) => ({ count: 0, increment: () => set((state) => ({ count: state.count + 1 })), decrement: () => set((state) => ({ count: state.count - 1 })), }));// Trong component function Counter() { const { count, increment, decrement } = useCounterStore(); return ( <div> <p>Count: {count}</p> <button onClick={increment}>+</button> <button onClick={decrement}>-</button> </div> ); }Không
<Provider>. Không slices. Chỉ pure functions.
import { createSlice, configureStore } from '@reduxjs/toolkit';const counterSlice = createSlice({ name: 'counter', initialState: { count: 0 }, reducers: { increment: (state) => { state.count += 1; }, decrement: (state) => { state.count -= 1; }, },});const store = configureStore({reducer: { counter: counterSlice.reducer },});export const { increment, decrement } = counterSlice.actions;// Trong componentimport { useSelector, useDispatch } from 'react-redux';function Counter() {const count = useSelector((state) => state.counter.count);const dispatch = useDispatch();return ( <div> <p>Count: {count}</p> <button onClick={() => dispatch(increment())}>+</button> <button onClick={() => dispatch(decrement())}>-</button> </div>);}Immer xử lý immutability. Nhưng vẫn cần cấu hình store.
const useUserStore = create((set) => ({users: [],fetchUsers: async () => { const response = await fetch('/api/users'); const data = await response.json(); set({ users: data });},}));Async trực tiếp. Không cần middleware bổ sung cho tác vụ cơ bản.
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'; export const userApi = createApi({ reducerPath: 'userApi', baseQuery: fetchBaseQuery({ baseUrl: '/api/' }), endpoints: (builder) => ({ getUsers: builder.query({ query: () => 'users' }), }), }); export const { useGetUsersQuery } = userApi;Caching, optimistic updates, auto-refetch — cấp enterprise.
Cả hai sử dụng shallow equality để giảm thiểu re-renders.
const count = useCounterStore((state) => state.count);import { useSelector } from 'react-redux';const count = useSelector((state) => state.counter.count);Zustand nhỉnh hơn về bundle size, nhưng Redux thắng ở complex selectors.
import { devtools } from 'zustand/middleware';const useStore = create(devtools((set) => ({ ... })));Profiler tích hợp của Redux theo dõi actions qua thời gian. Hoàn hảo cho team lớn debug race conditions.
// 1. Zustand không dùng selectorsfunction BadComponent() {const { count, users } = useCounterStore(); // Re-render khi BẤT KỲ thay đổi}// 2. Redux không dùng RTK// Reducers thủ công, thunks → Địa ngục boilerplate// 1. Zustand: Luôn select cụ thểconst count = useCounterStore((state) => state.count);// 2. Redux: Dùng RTK Query cho dataconst { data } = useGetUsersQuery();Zustand không giết chết Redux — nó bổ sung cho Redux.
Năm 2025, đơn giản thắng trận đánh, nhưng cấu trúc thắng cuộc chiến. Zustand giúp bạn ship nhanh hơn. Redux giúp bạn scale mãi mãi.
Đừng over-engineer app nhỏ. Đừng under-engineer app lớn. Đo lường nhu cầu. Chọn khôn ngoan.
Viết bởi Sophia Rodriguez