Menggunakan Zustand Sebagai State Management pada React/Next JS.
Tags: react, nextjs, javascript
Published: 2024-06-02 14:24:04


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2o7jl98fy8xhapbnugn1.png)


Pengelolaan state pada React JS atau Next JS tentunya adalah hal yang perlu kita perhatikan. Karena manajemen state merupakan salah satu hal yang cukup krusial dalam pengembangan aplikasi. Tentunya, sekarang kita akan coba sedikit berkenalan dengan Zustand yang memiliki deskripsi “_A small, fast and scalable bearbones state-management solution using simplified flux principles. Has a comfy API based on hooks, isn’t boilerplatey or opinionated_.”.

Tetapi sebelum kita masuk lebih dalam. Kita akan coba sedikit belajar bersama, kenapa state management itu diperlukan. Jika teman-teman sudah pernah membuat page dengan banyak component dan memiliki deep level of component pasti pernah merasakan hal yang cukup meresahkan. Dikarenakan ada case condition tertentu yang mengharuskan mengubah state dari grandchild ke parent. Jika teman-teman mengubah state dari grandchild ke parent dengan mengirimkan state dari parent → child → grandchild. Selamat anda masuk ke dalam kategori developer yang mengimplementasikan Props Drilling. Apakah ini sebuah pencapaian? tentu saja bukan, ini termasuk ke dalam kategori developer yang suka membuat ribet diri sendiri.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/trzkovxhkj5jqorhyfug.png)

Maka dengan permasalahan props drilling ini kita perlu menyelesaikan masalah tersebut dengan state management. Tapi perlu diketahui banyak sekali jenis-jenis state management yang bisa teman-teman gunakan selain Zustand yaitu Redux, Recoil, atau Jotai. Apakah Zustand lebih baik daripada state management yang lain? tentu saja jawabannya tidak. Dikarenakan masing-masing state management pasti punya kelebihan dan kekurangan masing-masing. Sehingga, kita perlu memilih dan menggunakan state management yang sesuai dengan kebutuhan aplikasi yang kita kembangkan.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vy1ej6pdrzmpy7byltbp.png)

---

### Persiapan

Sebelum mempelajari lebih lanjut pastikan sudah pernah tau tentang [context ](https://legacy.reactjs.org/docs/context.html) yang ada pada React JS. Saya berasumsi teman-teman pasti sudah tau tentang context dikarenakan sudah pernah dibahas pada sharing session.

### Instalasi Zustand

Jalankan perintah berikut untuk melakukan instalasi Zustand :

```bash
# Menggunakan yarn
yarn add zustand
```

```bash
# Menggunakan NPM
npm install zustand
```

### Implementasi useState vs Zustand

#### Mengubah state menggunakan useState Hooks

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t0enod94658a7wexsmuj.png)

Kita akan membuat tampilan seperti diatas dengan 3 komponen utama yaitu menampilkan total number, button increment, dan button decrement. Lalu kita akan melakukan passing useState hooks yang didefinisikan pada layout page Home.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r32xt5gd1izmq9zpjp74.png)

Kode program : 

```javascript
"use client"
import { useState } from 'react'
import ButtonIncrement from './components/ButtonIncrement';
import ButtonDecrement from './components/ButtonDecrement';

export default function Home() {
  const [count, setCount] = useState(0);
  return (
    <main className="flex flex-col items-center justify-center min-h-screen">
      <div>COUNT: {count}</div>
      <div className="flex flex-row my-2">
        <ButtonDecrement count={count} setCount={setCount} />
        <ButtonIncrement count={count} setCount={setCount} />
      </div>
    </main>
  )
}
```

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dwofwg7m82xmcx1rsikq.png)

Kode program : 

```javascript
import React, { Dispatch } from 'react'

interface Props { count: number; setCount: Dispatch<number> }
const ButtonDecrement = ({ count, setCount }: Props) => {
    return (
        <>
            <button 
                className="bg-white text-black rounded-sm px-2 py-2 mx-2" 
                onClick={() => setCount(count - 1)}>Decrement : {count}
            </button>
        </>
    )
}
export default ButtonDecrement
```

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ulj3p8sxnob8kbprhnf5.png)

Kode program :

```javascript
import React, { Dispatch } from 'react'

interface Props { count: number; setCount: Dispatch<number> }
const ButtonIncrement = ({ count, setCount }: Props) => {
   return (
      <>
         <button
            className="bg-white text-black rounded-sm px-2 py-2 mx-2"
            onClick={() => setCount(count + 1)}>Increment : {count}
            </button>
      </>
   )
}
export default ButtonIncrement
```

Dari kode program diatas apakah ada yang salah? jawabannya tidak ada. Tapi kalau pertanyaannya kita ubah menjadi, apakah kode program tersebut efektif untuk mengembangkan aplikasi dengan multi-level component yang banyak? jawabannya tidak efektif.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/plrryu95x9o7xwptxcb8.png)

Kita bisa melihat gambar diatas untuk menggambarkan multi-level component yang dimaksud sebelumnya. Jika kita mengirimkan useState hooks dari parent ke grandchild maka, ini sesuai dengan yang dimaksud props drilling pada penjelasan sebelumnya.

#### Mengubah state menggunakan Zustand

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/njat1hfnqmv6lz2516y8.png)

Kita akan membuat tampilan seperti diatas dengan 3 komponen utama yaitu menampilkan total number, button increment, dan button decrement. Lalu kita akan mendefinisikan store yang akan di-consume oleh component yang membutuhkan.

**Membuat File Store**

Kita akan membuat store yang akan digunakan di berbagai macam komponen yang membutuhkannya. Untuk penamaan useCounter merupakan penamaan yang bebas, jadi bisa disesuaikan dengan kebutuhan.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i7qam2z3gf7kyw8pr2po.png)

Kode program:

```javascript
import create from "zustand";

interface CounterState {
    count: number;
    increment: () => void;
    decrement: () => void;
}
const useCounter = create<CounterState>((set) => ({
    count: 0,
    increment: () => set((state) => ({ count: state.count + 1 })),
    decrement: () => set((state) => ({ count: state.count - 1 })),
}));
export default useCounter;
```

**Membuat Layout Home**

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n77qjwn7ual8w4wyundd.png)

Kode program: 

```javascript
"use client"
import ButtonIncrement from './components/ButtonIncrement';
import ButtonDecrement from './components/ButtonDecrement';
import useCounter from './stores/store';

export default function Home() {
  const counter = useCounter();
  return (
    <main className="flex flex-col items-center justify-center min-h-screen">
      <div>COUNT: {counter.count}</div>
      <div className="flex flex-row my-2">
        <ButtonDecrement />
        <ButtonIncrement />
      </div>
    </main>
  )
}
```

**Membuat Button Decrement Component**


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zypm4waxo0nsxqcsuu3i.png)

Kode program: 

```javascript
import React from 'react'
import useCounter from '../stores/store';

const ButtonDecrement = () => {
    const counter = useCounter();
    return (
        <>
            <button
                className="bg-white text-black rounded-sm px-2 py-2 mx-2"
                onClick={counter.decrement}>Decrement : {counter.count}
            </button>
        </>
    )
}
export default ButtonDecrement
```

**Membuat Button Increment Component**


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hv9qxlp9vhiaona4gg9g.png)

Kode program: 

```javascript
import React, { Dispatch } from 'react'
import useCounter from '../stores/store';

const ButtonIncrement = () => {
 const counter = useCounter();
 return (
  <>
   <button
    className="bg-white text-black rounded-sm px-2 py-2 mx-2"
    onClick={counter.increment}>Increment : {counter.count}
   </button>
  </>
 )
}
export default ButtonIncrement
```

Hanya dengan menggunakan 1 baris kode ini

```javascript
const counter = useCounter();

```

yang mana useCounter(); ini merupakan store yang sudah kita definisikan sebelumnya. Kita sudah bisa mengubah state dari komponen yang menggunakannya tanpa memperhatikan deep level of component-nya.

### Kesimpulan

Dengan menggunakan Zustand sebagai state management, kita bisa melihat perbedaan yang cukup signifikan antara penggunaan useState Hooks dan Zustand ketika mengubah sebuah state. Perbedaan yang cukup terlihat yaitu penggunaan Zustand jauh lebih efektif dan fleksibel, karena kita hanya perlu mendefinisikan store kemudian bisa digunakan di berbagai macam component tanpa memperhatikan deep level of component-nya. Tapi, jika skalabilitas aplikasi yang dikembangkan cukup kompleks maka, ada rekomendasi lain sebagai state management yaitu Redux Toolkit yang mana sejauh ini beberapa aplikasi yang mempunyai skalabilitas cukup kompleks masih menggunakan Redux dibandingkan Zustand.