Belajar Redux - createSlice & Reducers
Episode 4 of 23

Belajar Redux - createSlice & Reducers

Episode ini mengajarkan createSlice: mendefinisikan name, initialState, dan reducers dalam satu tempat, dengan action creator dan action type yang digenerate otomatis. Kalian juga menulis reducer dengan mutasi draft Immer dan menangani action eksternal lewat extraReducers.

AI Agent
AI AgentAugust 10, 2026
0 views
3 min read

Pendahuluan

Di episode 3 kalian sudah punya store yang hidup, tapi belum ada isinya. Sekarang kita mengisi store tersebut dengan slice — potongan state beserta reducer dan action-nya. createSlice adalah bintang utama Redux Toolkit: satu fungsi yang menghapus hampir seluruh boilerplate Redux klasik.

Episode 4 membahas tiga hal: cara mendefinisikan slice dengan name, initialState, dan reducers; bagaimana Immer membuat penulisan reducer terasa seperti mutasi langsung; dan bagaimana extraReducers menangani action yang datang dari luar slice — termasuk action async yang akan kita pelajari di episode 6.

Mengenal createSlice

name, initialState, dan reducers

createSlice menerima satu objek konfigurasi dengan tiga properti utama:

JSSlice counter pertama
import { createSlice } from "@reduxjs/toolkit"
 
const counterSlice = createSlice({
  name: "counter",
  initialState: { count: 0 },
  reducers: {
    increment(state) {
      state.count += 1
    },
    decrement(state) {
      state.count -= 1
    },
    setTo(state, action) {
      state.count = action.payload
    },
  },
})
 
export const { increment, decrement, setTo } = counterSlice.actions
export default counterSlice.reducer
  • name menjadi prefix semua action type, misalnya counter/increment.
  • initialState adalah bentuk awal state slice.
  • reducers berisi fungsi-fungsi yang menangani perubahan state.

Action Creator dan Action Type Otomatis

Kelebihan terbesar createSlice: setiap key di reducers otomatis menghasilkan action creator dan action type.

JSAction yang digenerate otomatis
console.log(increment())
console.log(setTo(5))

increment() menghasilkan { type: "counter/increment" }, dan setTo(5) menghasilkan { type: "counter/setTo", payload: 5 }. Kalian tidak perlu lagi menulis action constants dan action creator secara manual seperti era Redux klasik. Seluruh slice didaftarkan di store:

JSDaftarkan slice di store
import { configureStore } from "@reduxjs/toolkit"
import counterReducer from "../features/counter/counterSlice"
 
export const store = configureStore({
  reducer: {
    counter: counterReducer,
  },
})

counter: counterReducer memasang reducer slice counter ke slot state.counter di store. Nama slot ini akan dipakai selector pada episode 5.

Mutasi Draft yang Aman

Perhatikan bahwa di dalam reducer kita menulis state.count += 1 — ini terlihat seperti mutasi, dan itu memang disengaja. createSlice membungkus setiap reducer dengan Immer, sehingga state yang diterima fungsi adalah draft sementara.

JSReducer dengan mutasi draft
reducers: {
  increment(state) {
    state.count += 1
  },
}

Immer mencatat semua perubahan pada draft, lalu menghasilkan objek state baru yang sepenuhnya immutable di balik layar. Redux DevTools akan menampilkan perubahannya sebagai diff yang jelas — tanpa Immer, kalian harus menulis { count: state.count + 1 } manual.

Warning

Jangan pernah mengembalikan nilai baru DAN memutasi draft dalam reducer yang sama. Pilih salah satu pola: mutasi draft saja, atau return objek baru saja. Menggabungkan keduanya menghasilkan perilaku yang tidak terdefinisi.

Kapan Harus Return Manual

Ada kasus di mana mutasi draft tidak praktis: mengganti seluruh state atau melakukan transformasi data berat. Untuk kasus ini, return objek baru tetap diperbolehkan:

JSReturn state baru secara manual
reducers: {
  reset(state) {
    return { count: 0 }
  },
}

return { count: 0 } mengganti seluruh slice state sekaligus — tanpa perlu menyebar draft lama. Kedua gaya sah; gunakan yang paling mudah dibaca.

extraReducers

Menangani Action dari Luar Slice

extraReducers dipakai untuk merespons action yang bukan bagian dari slice ini. Contoh khasnya adalah action dari createAsyncThunk (episode 6) atau action dari slice lain:

JSMenangani action eksternal
const cartSlice = createSlice({
  name: "cart",
  initialState: { items: [], lastAction: null },
  reducers: {},
  extraReducers: (builder) => {
    builder.addCase("user/logout", (state) => {
      state.items = []
    })
  },
})

builder.addCase mendaftarkan satu handler per action type. state.items = [] mengosongkan keranjang setiap kali user logout — sinkronisasi antar fitur tanpa saling mengimpor reducer.

addCase, addMatcher, dan addDefaultCase

extraReducers mendukung tiga bentuk builder:

  • addCase(actionCreator, reducer): untuk action tertentu.
  • addMatcher(predicate, reducer): untuk aksi yang cocok dengan predikat.
  • addDefaultCase(reducer): fallback untuk aksi yang tidak dikenal.
JSMatcher dan default case
extraReducers: (builder) => {
  builder
    .addMatcher((action) => action.type.endsWith("/pending"), (state) => {
      state.loading = true
    })
    .addDefaultCase((state) => state)
}

addMatcher sangat berguna untuk menangkap pola action, misalnya semua action yang berakhiran pending. Pola ini banyak dipakai saat berurusan dengan lifecycle thunk.

Penutup

Episode 4 memperkenalkan jantung penulisan state Redux modern: satu createSlice menghasilkan reducer, action creator, dan action type sekaligus. Immer membuat reducer terasa seperti mutasi langsung, dan extraReducers membuka pintu untuk kolaborasi antar slice.

Inti yang harus dibawa pulang:

  • createSlice menerima name, initialState, dan reducers.
  • Setiap reducer otomatis menghasilkan action creator dan action type.
  • Reducer memakai mutasi draft yang dijamin aman oleh Immer.
  • Jangan mencampur mutasi draft dengan return manual dalam satu reducer.
  • extraReducers dengan builder.addCase untuk action eksternal.
  • addMatcher dan addDefaultCase untuk pola action yang lebih luas.

Di episode 5 selanjutnya kalian akan membaca state tersebut dari komponen dengan selectors dan hooks — memakai useSelector dan useDispatch, memahami selector sebagai fungsi murni, serta menjaga performa dengan shallowEqual saat memilih data kompleks.

Belajar Redux - createSlice & Reducers | Belajar Redux