Website logo

08 Feb 2024

Membuat
Global
State
Dengan
Zustand

Katakanlah kita lagi menghandle sebuah project website yang memiliki tampilan putih dengan warna-warna aksen yang cukup cerah. Namun suatu saat client minta kita agar website tersebut mendukung fitur Dark Theme, dimana ketika pengunjung website mengklik suatu tombol pada menu maka warna website yang tadinya dominan cerah tadi menjadi cenderung gelap agar lebih nyaman dimata.

import { useState } from 'react';

const Menu = () => {
  const [theme, setTheme] = useState('light');

  const toggleTheme = () => {
    setTheme(theme === 'dark' ? 'light' : 'dark');
  };

  return (
    <div>
      <h1>Menu</h1>

      <button
        style={{ backgroundColor: theme === 'dark' ? 'black' : 'white' }}
        type="button"
        onClick={toggleTheme}
      >
        Toggle Theme
      </button>
    </div>
  );
};

export default Menu;

Pada implementasi komponen diatas, kita bisa lihat kalau tombol “Toggle Theme” diklik, maka warna tombol tersebut akan berubah berdasarkan value dari state “theme”. Namun disini ada masalah, state “theme” hanya dapat diakses oleh komponen Menu itu sendiri sedangkan value dari state “theme” perlu diakses oleh komponen lainnya.

Pada dasarnya state memiliki dua jenis, yaitu local state dan global state. Local state adalah sebuah state yang diterapkan didalam suatu komponen dan dapat diakses oleh komponen itu sendiri beserta komponen-komponen child yang ada didalamnya melalui props. Sedangkan global state adalah state yang dapat diakses oleh semua komponen yang ada.

Dalam kasus ini, kita perlu mengubah state “theme” dari local state menjadi global state dengan menggunakan bantuan sebuah state management library bernama Zustand.

Apa itu Zustand

Zustand adalah sebuah state management library untuk React yang dapat kita gunakan untuk menyimpan state secara global. Zustand lebih mudah untuk digunakan dan dikonfigurasi dibanding state management library lainnya seperti Redux dan memiliki dokumentasi yang cukup bagus.

Selain itu, Zustand juga memiliki beberapa middleware yang cukup berguna seperti persist middleware yang dapat kita gunakan untuk mensinkronisasi state di Zustand dengan storage seperti LocalStorage dan lain sebagainya. Menurut saya, Zustand juga lebih nyaman untuk digunakan dibandingkan Context API. Karena kemudahanya, saya sering menggunakannya di project-project saya.

Cara Menerapkan Zustand

Pertama, kita perlu install Zustand melalui npm di project React kita.

npm install zustand

Lalu buat file slice untuk menyimpan state “theme” kita pada directory store/slices/theme.slice.ts. Slice merupakan bagian dari store yang biasanya berisikan state yang kita buat beserta function yang digunakan untuk mengubah value dari state yang bersangkutan. Disini kita buat Light Theme sebagai default theme.

import { StateCreator } from 'zustand';

type Theme = 'dark' | 'light';

export type ThemeSlice = {
  theme: Theme;
  setTheme: (value: Theme) => void;
};

export const createThemeSlice: StateCreator<
  ThemeSlice,
  [],
  [],
  ThemeSlice
> = (set) => ({
  theme: 'light',
  setTheme: (theme) => set({ theme }),
});

Kemudian, kita perlu juga untuk menambahkan slice yang telah kita buat tersebut kedalam file store pada directory store/store.ts.

import { create } from 'zustand';

import { ThemeSlice, createThemeSlice } from './slices/theme.slice';

export const useStore = create<ThemeSlice>()(
  (...a) => ({
    ...createThemeSlice(...a),
  })
);

Setelah itu baru kita dapat menggunakan global state yang telah kita buat dengan Zustand.

// Kita import store yang telah kita buat tadi.
import { useStore } from "../../store"; 

const Menu = () => {
  const theme = useStore((state) => state.theme);
  const setTheme = useStore((state) => state.setTheme);

  const toggleTheme = () => {
    setTheme(theme === 'dark' ? 'light' : 'dark');
  };

  return (
    <div>
      <h1>Menu</h1>

      <button
        style={{ backgroundColor: theme === 'dark' ? 'black' : 'white' }}
        type="button"
        onClick={toggleTheme}
      >
        Toggle Theme
      </button>
    </div>
  );
};

export default Menu;

Kita bisa juga terapkan di komponen lainnya. Dengan ini, state “theme” yang tadinya local state sekarang menjadi global state.

import { useStore } from "../../store"; 

const Header = () => {
  const theme = useStore((state) => state.theme);

  return (
    <div>
      <h1
				style={{
					backgroundColor: theme === 'dark' ? 'white' : 'black'
				}}
			>
				My Website
			</h1>
    </div>
  );
};

export default Header;

Penerapan State Yang Persisten

Global state telah berjalan dengan baik, namun ketika kita refresh website kita di browser, maka state akan kembali ke initial value yaitu “light” dan membuat Light Theme kembali aktif. Seharusnya ketika pengunjung website memilih Dark Theme dan merefresh websitenya, maka Dark Theme harusnya otomatis aktif. Disini kita dapat membuat Zustand untuk menyimpan statenya ke dalam LocalStorage di browser jadi sehingga ketika website direfresh, maka Zustand akan mengecek statenya yang telah disimpan di LocalStorage lalu mengambilnya untuk ditaruh ke dalam store. Kita hanya perlu menggunakan persist middleware pada store yang kita buat tadi.

import { create } from "zustand";
import { persist } from "zustand/middleware";

import { ThemeSlice, createThemeSlice } from "./slices/theme.slice";

export const useStore = create<ThemeSlice>()(
  persist(
    (...a) => ({
      ...createThemeSlice(...a),
    }),
    {
      name: "main-storage",
    }
  )
);

Secara default, persist middleware menggunakan LocalStorage sebagai storagenya, namun kita dapat membuat persist middleware untuk menggunakan storage lain seperti SessionStorage bila kita menginginkannya.

Kesimpulan

Dengan menggunakan Zustand, penerapan global state menjadi jauh lebih mudah dibandingkan dengan menggunakan Redux. Selain itu Zustand memiliki banyak fitur yang dapat kita gunakan untuk menunjang pengembangan suatu project.