Best Practice-nya Zustand Persist di Next.js Sesuai Dokumentasi
Pada artikel sebelumnya, Saya membahas bagaimana Saya menggunakan Zustand untuk dapat mengelola data yang telah di-input user.
Dalam pengerjaan proyek, Saya menyadari perlu untuk menyimpan data di localStorage agar user tidak perlu melakukan input ulang jika user melakukan refresh.
Setelah mencari tahu, itu dapat diselesaikan dengan Zustand Persist.
Saya menggunakan Zustand sesuai yang didokumentasikan dengan anggapan bahwa itu best practice.
Lalu masalahnya muncul, yaitu cara yang Saya gunakan adalah cara yang berbeda untuk menggunakan Zustand Persist.
Jadi, berikut adalah cara untuk menggunakan Zustand Persist di Next.js dengan TypeScript sesuai dokumentasi.
Konfigurasi tsconfig.json
Berikut adalah konfigurasi file tsconfig.json yang berada di root folder:
{
"compilerOptions": {
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"plugins": [
{
"name": "next"
}
],
"paths": {
"@/*": ["./src/*"]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
}
Instalasi Zustand
Jalankan command berikut di terminal:
npm install zustand
Konfigurasi Store
Buat file counter-store.ts yang berisi code berikut di folder src/app/_stores:
Perlu diingat bahwa folder yang berawalan underscore adalah private folder agar tidak terbaca sebagai route.
import { createStore } from 'zustand/vanilla';
import { persist, createJSONStorage } from 'zustand/middleware';
export type CounterState = {
count: number;
};
export type CounterActions = {
decrementCount: () => void;
incrementCount: () => void;
};
export type CounterStore = CounterState & CounterActions;
export const initCounterStore = (): CounterState => {
return { count: new Date().getFullYear() };
};
export const defaultInitState: CounterState = {
count: 0,
};
export const createCounterStore = (
initState: CounterState = defaultInitState
) => {
return createStore<CounterStore>()(
persist(
(set) => ({
...initState,
decrementCount: () => set((state) => ({ count: state.count - 1 })),
incrementCount: () => set((state) => ({ count: state.count + 1 })),
}),
{
name: 'counter-store',
storage: createJSONStorage(() => localStorage),
skipHydration: true,
}
)
);
};
Konfigurasi Provider
Buat file counter-store-provider.tsx yang berisi code berikut di folder src/app/_providers:
'use client';
import { type ReactNode, createContext, useContext, useState } from 'react';
import { useStore } from 'zustand';
import { createCounterStore } from '@/app/_stores/counter-store';
type CounterBoundStore = ReturnType<typeof createCounterStore>;
export const CounterStoreContext = createContext<CounterBoundStore | null>(
null
);
export interface CounterStoreProviderProps {
children: ReactNode;
}
export const CounterStoreProvider = ({
children,
}: CounterStoreProviderProps) => {
const [counterStore] = useState(() => createCounterStore());
return (
<CounterStoreContext.Provider value={counterStore}>
{children}
</CounterStoreContext.Provider>
);
};
// prettier-ignore
export const useCounterStore = <T,>(
selector: (store: ReturnType<CounterBoundStore['getState']>) => T
): [T, CounterBoundStore] => {
const counterStore = useContext(CounterStoreContext);
if (!counterStore) {
throw new Error(`useCounterStore must be use within CounterStoreProvider`);
}
return [useStore(counterStore, selector), counterStore];
};
Penggunaan Provider
Buat file layout.tsx yang berisi code berikut di folder route milikmu, misalnya src/app:
import { CounterStoreProvider } from './_providers/counter-store-provider';
...
export default function Layout({
children,
}: {
children: React.ReactNode;
}) {
return (
...
<CounterStoreProvider>{children}</CounterStoreProvider>
...
);
}
Konfigurasi Page
Terakhir gunakan variable yang tadi sudah dibuat di store, ke halaman. Contohnya src/app/page.tsx yang berisi code berikut:
'use client';
import { useLayoutEffect } from 'react';
import { useCounterStore } from '@/app/_providers/counter-store-provider';
export const HomePage = () => {
const [{ count, incrementCount, decrementCount }, counterStore] =
useCounterStore((state) => state);
useLayoutEffect(() => {
counterStore.persist?.rehydrate();
}, []);
return !counterStore.persist?.hasHydrated() ? (
'Loading...'
) : (
<div>
Count: {count}
<hr />
<button type="button" onClick={() => void incrementCount()}>
Increment Count
</button>
<button type="button" onClick={() => void decrementCount()}>
Decrement Count
</button>
</div>
);
};
Sekian untuk cara mengimplementasikan Zustand Persist berdasarkan kebutuhan saya saat menggunakan Zustand.
Kode di atas bersumber dari sini.
Related Posts
Implementasi Light dan Dark Mode di Web itu Mudah
Pendahuluan# Sudah umum kalau device terkini memiliki fitur light dan dark mode terpasang di operating system bawaan. Masalah# Tapi terkadang fitur ini masih belum kita bisa temukan di website yang ki...
3 Kelebihan dan Kekurangan Menggunakan CSS Variable
TL;DR# Walaupun umum digunakan oleh template-template front-end di internet dan website bisnis skala besar, penggunaan variable masih jarang diterapkan pada pengembangan website dari scratch. Ini dika...