Ringkasan Utama
Untuk proyek React baru di 2026, Zustand adalah pilihan default yang masuk akal: tanpa Provider, store cukup empat baris, dan bundle kecil. Redux Toolkit tetap relevan di tim besar yang butuh struktur slice yang ketat, devtools time-travel yang matang, dan RTK Query. Apa pun pilihannya, data server sebaiknya ditangani TanStack Query, bukan disimpan di client store global.
Setiap beberapa bulan selalu muncul diskusi apakah Redux sudah mati, dan jawabannya selalu sama: tidak, tapi pilihan default untuk proyek React baru di 2026 jarang lagi jatuh ke Redux Toolkit. Zustand menjadi titik awal yang praktis bagi kebanyakan tim, sementara Redux Toolkit tetap pilihan tepat begitu aplikasi tumbuh besar dan butuh struktur yang dipaksakan di antara banyak kontributor.
Artikel ini membahas perbedaan nyata yang benar-benar berpengaruh sehari-hari: seberapa banyak kode yang harus ditulis di masing-masing library, seperti apa proses debugging dengan devtools masing-masing, seberapa besar beban yang ditambahkan ke bundle, dan kesalahan yang paling sering saya lihat - menggunakan salah satu library untuk menyimpan data yang sebenarnya berasal dari API dan seharusnya berada di lapisan caching seperti TanStack Query. Semua ini bukan soal library mana yang secara objektif lebih unggul; ini soal menyesuaikan alat dengan ukuran dan bentuk masalah yang benar-benar kamu hadapi.
Boilerplate: seberapa banyak kode yang benar-benar ditulis
Titik awal Zustand adalah fungsi biasa yang mengembalikan state beserta fungsi untuk mengubahnya. Tidak ada Provider yang perlu membungkus aplikasi, tidak ada action type yang perlu didefinisikan, dan tidak ada pemanggilan dispatch di titik pemakaian - komponen cukup memanggil store seperti hook biasa dan membaca bagian state yang dibutuhkan.
// Zustand: a full store in ~10 lines, no Provider
import { create } from "zustand";
interface CartState {
items: string[];
addItem: (item: string) => void;
clear: () => void;
}
export const useCartStore = create<CartState>((set) => ({
items: [],
addItem: (item) => set((state) => ({ items: [...state.items, item] })),
clear: () => set({ items: [] }),
}));
// Usage in a component, no wrapping context needed
function CartBadge() {
const items = useCartStore((state) => state.items);
return <span>{items.length}</span>;
}Fungsi createSlice di Redux Toolkit memangkas sebagian besar seremoni yang membuat Redux klasik menyakitkan - kamu tidak lagi menulis manual action type, action creator, atau blok switch, dan Immer memungkinkan penulisan kode yang tampak mutatif namun tetap dikonversi menjadi update immutable di baliknya. Meski begitu, kamu tetap perlu menyusun sebuah slice, mendaftarkannya ke configureStore, dan membungkus pohon komponen dengan Provider sebelum satu pun komponen bisa membacanya. Untuk fitur kecil, ini jelas lebih banyak kode dibanding satu store Zustand.
// Redux Toolkit: createSlice cuts classic Redux boilerplate,
// but you still need a slice, a typed store, and Provider wiring
import { createSlice, configureStore } from "@reduxjs/toolkit";
const cartSlice = createSlice({
name: "cart",
initialState: { items: [] as string[] },
reducers: {
addItem: (state, action) => {
state.items.push(action.payload);
},
clear: (state) => {
state.items = [];
},
},
});
export const { addItem, clear } = cartSlice.actions;
export const store = configureStore({ reducer: { cart: cartSlice.reducer } });
// App root must be wrapped in <Provider store={store}>Devtools: seperti apa debugging sebenarnya
Kedua library terhubung ke ekstensi browser Redux DevTools yang sama, tapi kedalaman integrasinya berbeda. configureStore milik Redux Toolkit menghubungkan ekstensi ini secara otomatis dengan dukungan penuh untuk time travel, replay action, dan diff state di setiap action yang di-dispatch.
- Redux Toolkit: setiap action muncul sebagai entri berlabel dengan diff sebelum dan sesudah, dan kamu bisa mundur melalui seluruh sesi.
- Zustand: middleware devtools opsional mengirim snapshot state ke ekstensi yang sama, berlabel sesuai nama action yang kamu berikan ke set, yang mencakup inspeksi state dan pencatatan action dasar.
- Tidak satu pun menggantikan console log atau React DevTools untuk render di level komponen - keduanya berfokus pada pelacakan perubahan state seiring waktu, bukan performa render.
Jika tim kamu sangat mengandalkan debugging time-travel untuk mereproduksi bug produksi dari dump state yang diekspor, alur kerja ini jauh lebih matang di Redux Toolkit. Middleware devtools Zustand memberi visibilitas, bukan pipeline replay yang lengkap.
Ukuran bundle: selisihnya nyata tapi jarang menentukan
Paket inti Zustand sangat kecil dan tidak memerlukan dependensi wajib selain React itu sendiri. Redux Toolkit menggabungkan configureStore, createSlice, Immer, dan Redux Thunk sekaligus, dan hampir selalu kamu tambahkan react-redux di atasnya untuk menghubungkannya ke komponen. RTK Query, jika dipakai, adalah paket tambahan lagi di atas semua itu.
| Library | Paket inti | Setup produksi umum |
|---|---|---|
| Zustand | Inti minimal, tanpa paket peer wajib selain React | Store inti plus middleware devtools dan persist opsional |
| Redux Toolkit | Menggabungkan Immer dan Redux Thunk secara default | Redux Toolkit plus react-redux untuk binding komponen |
| Redux Toolkit + RTK Query | Menambahkan lapisan pengambilan dan caching data di atasnya | Setup penuh saat RTK Query menggantikan logika fetch manual |
Tumpang tindih yang jarang dibahas: server state
Kesalahan arsitektur paling umum yang saya lihat saat code review tidak ada hubungannya dengan library mana yang dipilih - melainkan menyimpan data server di dalam global client state sama sekali. Data yang diambil dari API bersifat remote, asinkron, dan bisa berubah kapan saja oleh pengguna lain, yang membuatnya berbeda secara fundamental dari state UI lokal seperti nilai form atau toggle sidebar. Tim yang menyimpan record hasil fetch di slice Zustand atau Redux biasanya akhirnya membuat sendiri flag loading, logika retry, dan invalidasi cache, yang justru adalah pekerjaan yang sudah dilakukan dengan benar oleh library pengambilan data khusus.
Dokumentasi resmi TanStack Query menegaskan garis pemisah ini secara eksplisit: library state management tradisional bekerja baik untuk client state tapi tidak dibangun untuk menangani caching, refetch di background, deduplikasi, atau staleness untuk data server. Itu adalah masalah berbeda dengan solusi yang berbeda, dan mencoba menyelesaikannya secara manual di dalam store Zustand atau slice Redux biasanya berarti membangun ulang versi yang lebih buruk dari apa yang sudah dilakukan library caching khusus.
Pembagian praktis yang bekerja baik di produksi: TanStack Query menangani apa pun yang berasal dari panggilan fetch - daftar, record, cursor paginasi - sementara Zustand atau Redux Toolkit menangani semua hal yang hanya hidup di browser, seperti visibilitas modal, state form multi-langkah, atau preferensi tema.
Redux Toolkit sendiri bahkan punya jawaban untuk masalah ini lewat RTK Query, yang dengan sengaja dibangun sebagai concern terpisah yang diletakkan di atas store, bukan dicampur ke dalam slice biasa. Pilihan desain dari tim Redux sendiri ini adalah sinyal kuat bahwa server state dan client state pantas mendapat alat yang berbeda, apa pun library client-state yang akhirnya kamu pilih.
Saat global state sama sekali berlebihan
Sebelum menggunakan salah satu library, ada baiknya bertanya apakah kamu benar-benar butuh global state. Tiga pengecekan yang saya lakukan bersama tim sebelum menambahkan dependensi baru:
- Apakah state ini benar-benar dibagikan ke komponen yang tidak berdekatan dalam pohon komponen, atau bisa saja hidup di komponen induk dan diturunkan lewat props.
- Apakah state ini perlu bertahan lintas perubahan route, atau tidak masalah jika reset saat pengguna pindah dari satu halaman ke halaman lain.
- Apakah ini sebenarnya data server yang menyamar - berasal dari respons API - yang berarti seharusnya berada di TanStack Query, bukan di global client store sama sekali.
Pasangan useContext dan useState, dibatasi hanya pada bagian pohon komponen yang benar-benar membutuhkannya, seringkali sudah cukup untuk satu halaman atau satu fitur. Gunakan Zustand atau Redux Toolkit ketika state memang perlu dibaca dan ditulis dari cabang pohon komponen yang tidak saling berhubungan.
Kesimpulannya
Untuk proyek baru yang dimulai hari ini tanpa investasi Redux sebelumnya, Zustand adalah pilihan default yang masuk akal. Ukuran bundle yang kecil, tidak perlunya Provider, dan definisi store empat baris berarti lebih sedikit kode yang harus dirawat serta hambatan yang lebih rendah bagi kontributor baru untuk memahami di mana state disimpan.
Redux Toolkit pantas dipakai di tim dan basis kode yang lebih besar, di mana struktur slice yang dipaksakan, devtools matang dengan time-travel penuh, dan lapisan pengambilan data RTK Query yang opinionated berfungsi sebagai pagar pengaman begitu puluhan engineer menyentuh store yang sama. Ia menukar sebagian seremoni dengan konsistensi, dan di tim yang cukup besar pertukaran itu sepadan. Struktur ekstra ini juga terbayar saat onboarding: karyawan baru yang membaca basis kode Redux Toolkit bisa menebak di mana suatu state berada hanya dengan mengetahui konvensi penamaan slice, yang lebih sulit diterapkan di sekumpulan store Zustand yang dibuat secara independen tanpa lapisan konvensi tambahan dari tim itu sendiri.
Apa pun yang kamu pilih, tahan keinginan untuk memigrasikan store yang sudah berjalan baik hanya karena library baru sedang tren. Migrasi membawa risiko nyata untuk manfaat yang marjinal, kecuali setup saat ini benar-benar menimbulkan masalah terukur - onboarding yang lambat, bug state yang sering, atau ukuran bundle yang benar-benar merugikan metrik waktu muat.

