REACT STATE-MANAGEMENT FRONTEND JAVASCRIPT WEB-DEVELOPMENT PERFORMANCE SCALABILITY DEVELOPER-EXPERIENCE MODERN-WEB ATOM-BASED-STATE

Atom-based State Management dengan Recoil/Jotai: Membangun Aplikasi React yang Cerdas dan Skalabel

⏱️ 12 menit baca
👨‍💻

Atom-based State Management dengan Recoil/Jotai: Membangun Aplikasi React yang Cerdas dan Skalabel

1. Pendahuluan

Jika Anda seorang developer React, Anda pasti akrab dengan tantangan dalam mengelola state aplikasi. Mulai dari “prop drilling” yang membuat kode berantakan, boilerplate yang banyak di Redux, hingga keterbatasan Context API untuk skenario kompleks dengan pembaruan yang sering. Seiring bertambahnya fitur dan skala aplikasi, state management bisa menjadi “neraka” yang memengaruhi performa dan developer experience (DX).

Untungnya, ekosistem React terus berinovasi. Dua library yang semakin populer dan menawarkan paradigma baru untuk state management adalah Recoil (dari Facebook) dan Jotai (lebih minimalis). Keduanya mengusung konsep atom-based state management, sebuah pendekatan yang terinspirasi langsung dari cara kerja React itu sendiri. Mereka memungkinkan Anda mengelola state dengan granularitas tinggi, performa optimal, dan kode yang lebih bersih.

Artikel ini akan membawa Anda menyelami dunia atom-based state management. Kita akan memahami apa itu atom, selector, mengapa pendekatan ini begitu powerful, dan bagaimana Anda bisa mulai menggunakannya untuk membangun aplikasi React yang lebih cerdas dan skalabel.

2. Apa Itu Atom-based State Management?

Bayangkan state aplikasi Anda sebagai sekumpulan butiran pasir yang sangat kecil, bukan satu bongkahan besar. Setiap butiran pasir (atom) adalah unit state independen yang dapat dibaca dan diperbarui secara terpisah.

Tradisionalnya, banyak solusi state management cenderung mengumpulkan seluruh state aplikasi ke dalam satu objek besar (seperti di Redux Store) atau beberapa objek besar. Ketika sebagian kecil dari state itu berubah, seringkali memicu render ulang yang tidak perlu pada banyak komponen karena mereka “mendengarkan” perubahan pada objek state yang lebih besar.

Atom-based state management mengubah perspektif ini. Daripada mengelola satu global store yang monolitik, kita mendefinisikan state dalam unit-unit yang sangat kecil dan terisolasi, yang disebut atom.

📌 Analogi: Jika aplikasi Anda adalah sebuah rumah, state di global store tradisional seperti satu saklar lampu utama yang mengontrol semua lampu di rumah. Ketika Anda ingin menyalakan lampu dapur, Anda harus menyalakan saklar utama, dan itu memicu perubahan pada semua lampu lain (meskipun mereka tidak berubah).

Dengan atom-based state management, setiap lampu memiliki saklarnya sendiri (atom). Ketika Anda menyalakan lampu dapur, hanya lampu dapur yang terpengaruh, tanpa memengaruhi atau memicu “pemeriksaan” pada lampu di kamar tidur atau ruang tamu. Ini jauh lebih efisien!

Setiap komponen kemudian dapat “berlangganan” (subscribe) hanya pada atom-atom spesifik yang mereka butuhkan. Jika sebuah atom berubah, hanya komponen yang berlangganan atom tersebut yang akan mengalami re-render. Ini adalah kunci untuk performa yang lebih baik dan re-render yang lebih efisien di aplikasi React Anda.

3. Konsep Dasar: Atom dan Selector

Dua pilar utama dalam atom-based state management adalah atom dan selector.

3.1. Atom: Unit State Terkecil

Atom adalah unit state yang dapat di-subscribe oleh komponen dan juga diperbarui. Atom adalah sumber kebenaran (source of truth) untuk state tertentu. Mereka mirip dengan state lokal dalam useState React, tetapi dengan kemampuan untuk diakses dan dibagi di mana saja dalam pohon komponen Anda.

Contoh dengan Jotai:

// atoms.js
import { atom } from 'jotai';

// Atom untuk menyimpan nilai counter
export const countAtom = atom(0);

// Atom untuk menyimpan status dark mode
export const darkModeAtom = atom(false);

Untuk menggunakan atom ini di komponen React Anda:

// MyCounter.jsx
import React from 'react';
import { useAtom } from 'jotai';
import { countAtom } from './atoms';

function MyCounter() {
  const [count, setCount] = useAtom(countAtom); // Menggunakan atom countAtom

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(c => c + 1)}>Tambah</button>
      <button onClick={() => setCount(c => c - 1)}>Kurang</button>
    </div>
  );
}

export default MyCounter;

Keunggulan Atom:

3.2. Selector: Derived State yang Fleksibel

Selector adalah fungsi pure yang mengambil state dari satu atau lebih atom (atau selector lain) dan melakukan transformasi data. Mereka memungkinkan Anda untuk mendapatkan derived state (state turunan) tanpa perlu menyimpan state tersebut secara eksplisit. Selector akan secara otomatis re-evaluate dan membuat komponen yang berlangganan untuk re-render hanya ketika atom atau selector yang menjadi dependensinya berubah.

Contoh dengan Jotai:

// atoms.js (lanjutan)
import { atom } from 'jotai';

export const countAtom = atom(0);
export const darkModeAtom = atom(false);

// Selector untuk mendapatkan status apakah counter genap atau ganjil
export const isEvenCountSelector = atom((get) => get(countAtom) % 2 === 0);

// Selector untuk mendapatkan teks sambutan berdasarkan dark mode
export const greetingSelector = atom((get) =>
  get(darkModeAtom) ? 'Selamat malam!' : 'Selamat pagi!'
);

Untuk menggunakan selector ini di komponen React Anda:

// MyStatus.jsx
import React from 'react';
import { useAtomValue } from 'jotai'; // useAtomValue hanya untuk membaca
import { isEvenCountSelector, greetingSelector } from './atoms';

function MyStatus() {
  const isEven = useAtomValue(isEvenCountSelector);
  const greeting = useAtomValue(greetingSelector);

  return (
    <div>
      <p>{greeting}</p>
      <p>Count is: {isEven ? 'Genap' : 'Ganjil'}</p>
    </div>
  );
}

export default MyStatus;

💡 Keunggulan Selector:

4. Mengapa Menggunakan Recoil/Jotai? Keunggulan Utama

Setelah memahami dasar-dasarnya, mari kita gali lebih dalam mengapa pendekatan atom-based ini bisa menjadi game changer untuk aplikasi React Anda:

  1. Granularitas dan Performa Optimal:

    • ❌ Masalah Tradisional: Dengan Context API atau Redux, seringkali seluruh subtree komponen akan re-render bahkan jika hanya sebagian kecil state yang berubah.
    • ✅ Solusi Atom-based: Hanya komponen yang secara eksplisit berlangganan pada atom atau selector yang berubah akan di-re-render. Ini meminimalkan pekerjaan yang tidak perlu dan menghasilkan aplikasi yang jauh lebih cepat dan responsif.
  2. Fleksibilitas dan Skalabilitas:

    • Mudah untuk membuat atom baru dan menggabungkannya sesuai kebutuhan. Anda tidak perlu memikirkan struktur global store yang kaku sejak awal.
    • Cocok untuk aplikasi besar dengan banyak fitur yang berbeda, karena setiap fitur dapat memiliki state atomnya sendiri tanpa memengaruhi fitur lain.
  3. Derived State yang Kuat dan Efisien:

    • Selector adalah cara elegan untuk mengelola logika bisnis yang kompleks yang bergantung pada berbagai bagian state.
    • Dengan fitur caching bawaan, Anda mendapatkan performa tanpa harus memikirkan memoization manual seperti useMemo atau useCallback secara berlebihan untuk state turunan.
  4. Integrasi Native dengan React:

    • API Recoil dan Jotai terasa seperti ekstensi alami dari React Hooks (useState, useContext). Ini membuat learning curve lebih landai bagi developer React.
    • Dirancang untuk bekerja dengan fitur-fitur modern React seperti Concurrent Mode dan Suspense, memungkinkan pengalaman pengguna yang lebih mulus (misalnya, loading state otomatis saat fetching data asinkron).
  5. Boilerplate Minimal:

    • Dibandingkan dengan Redux yang seringkali membutuhkan reducer, action creator, middleware, dan selector yang terpisah, Recoil/Jotai seringkali lebih ringkas dan langsung ke inti masalah. Anda dapat mendefinisikan state dan logikanya di satu tempat.

5. Recoil vs. Jotai: Memilih yang Tepat

Baik Recoil maupun Jotai sama-sama mengimplementasikan filosofi atom-based state management. Namun, ada beberapa perbedaan yang bisa menjadi pertimbangan Anda:

Kapan memilih yang mana?

Keduanya adalah pilihan yang sangat baik dan akan memberikan manfaat performa dan DX yang serupa. Pilihan seringkali tergantung pada preferensi pribadi dan kebutuhan spesifik proyek.

6. Contoh Praktis: Aplikasi Counter Sederhana dengan Jotai

Mari kita buat contoh sederhana menggunakan Jotai untuk melihat bagaimana atom dan selector bekerja dalam praktik.

Pertama, pastikan Anda sudah menginstal Jotai:

npm install jotai
# atau
yarn add jotai

6.1. Mendefinisikan Atom dan Selector

Buat file src/store/atoms.js:

// src/store/atoms.js
import { atom } from 'jotai';

// Atom untuk nilai counter
export const countAtom = atom(0);

// Atom untuk status apakah counter aktif (misal: lebih dari 0)
export const isActiveAtom = atom((get) => get(countAtom) > 0);

// Selector untuk nilai counter yang dikalikan dua
export const doubledCountSelector = atom((get) => get(countAtom) * 2);

// Selector untuk status apakah counter genap atau ganjil
export const paritySelector = atom((get) =>
  get(countAtom) % 2 === 0 ? 'Genap' : 'Ganjil'
);

// Atom asinkron untuk mengambil data dari API (contoh)
export const usersAtom = atom(async () => {
  const response = await fetch('https://jsonplaceholder.typicode.com/users');
  const data = await response.json();
  return data;
});

// Selector untuk mendapatkan jumlah user
export const userCountSelector = atom((get) => get(usersAtom).length);

6.2. Menggunakan Atom dan Selector di Komponen

Buat komponen CounterDisplay.jsx:

// src/components/CounterDisplay.jsx
import React from 'react';
import { useAtomValue } from 'jotai';
import { countAtom, doubledCountSelector, paritySelector } from '../store/atoms';

function CounterDisplay() {
  const count = useAtomValue(countAtom); // Membaca nilai atom
  const doubledCount = useAtomValue(doubledCountSelector); // Membaca nilai selector
  const parity = useAtomValue(paritySelector); // Membaca nilai selector

  return (
    <div style={{ border: '1px solid #ccc', padding: '15px', margin: '10px', borderRadius: '5px' }}>
      <h3>Display Counter</h3>
      <p>Current Count: **{count}**</p>
      <p>Doubled Count: **{doubledCount}**</p>
      <p>Parity: **{parity}**</p>
    </div>
  );
}

export default CounterDisplay;

Buat komponen CounterControls.jsx:

// src/components/CounterControls.jsx
import React from 'react';
import { useAtom, useSetAtom } from 'jotai';
import { countAtom, isActiveAtom } from '../store/atoms';

function CounterControls() {
  const [count, setCount] = useAtom(countAtom); // Membaca dan menulis atom
  const isActive = useAtomValue(isActiveAtom); // Membaca nilai selector

  const increment = () => setCount((prev) => prev + 1);
  const decrement = () => setCount((prev) => prev - 1);
  const reset = () => setCount(0);

  return (
    <div style={{ border: '1px solid #007bff', padding: '15px', margin: '10px', borderRadius: '5px' }}>
      <h3>Counter Controls</h3>
      <p>Status: {isActive ? 'Aktif' : 'Tidak Aktif'}</p>
      <button onClick={increment}>Increment</button>
      <button onClick={decrement}>Decrement</button>
      <button onClick={reset}>Reset</button>
    </div>
  );
}

export default CounterControls;

Buat komponen UserFetcher.jsx:

// src/components/UserFetcher.jsx
import React, { Suspense } from 'react';
import { useAtomValue } from 'jotai';
import { usersAtom, userCountSelector } from '../store/atoms';

function UserList() {
  const users = useAtomValue(usersAtom);
  const userCount = useAtomValue(userCountSelector);

  return (
    <div>
      <h4>Daftar Pengguna ({userCount} total)</h4>
      <ul>
        {users.map(user => (
          <li key={user.id}>{user.name} ({user.email})</li>
        ))}
      </ul>
    </div>
  );
}

function UserFetcher() {
  return (
    <div style={{ border: '1px solid #28a745', padding: '15px', margin: '10px', borderRadius: '5px' }}>
      <h3>Fetch Data Asynchronous</h3>
      <Suspense fallback={<div>Loading users...</div>}>
        <UserList />
      </Suspense>
    </div>
  );
}

export default UserFetcher;

Gabungkan di App.js:

// src/App.js
import React from 'react';
import CounterDisplay from './components/CounterDisplay';
import CounterControls from './components/CounterControls';
import UserFetcher from './components/UserFetcher';

function App() {
  return (
    <div style={{ fontFamily: 'Arial, sans-serif', padding: '20px' }}>
      <h1>Aplikasi Jotai Sederhana</h1>
      <CounterControls />
      <CounterDisplay />
      <UserFetcher />
    </div>
  );
}

export default App;

Penjelasan:

Ini adalah contoh sederhana, tetapi menunjukkan bagaimana atom-based state management memungkinkan Anda untuk mendefinisikan state secara granular dan mengelolanya dengan cara yang modular dan efisien.

Kesimpulan

Atom-based state management dengan library seperti Recoil dan Jotai menawarkan pendekatan yang menyegarkan untuk mengelola state di aplikasi React. Dengan fokus pada granularitas, derived state yang efisien, dan integrasi native dengan React, mereka memecahkan banyak masalah yang sering ditemui dalam solusi state management tradisional.

Jika Anda mencari cara untuk meningkatkan performa aplikasi React Anda, mengurangi boilerplate, dan meningkatkan developer experience, maka mempelajari dan mengadopsi Recoil atau Jotai bisa menjadi langkah yang sangat tepat. Cobalah sendiri dan rasakan perbedaannya dalam membangun aplikasi React yang lebih cerdas, lebih cepat, dan lebih mudah dikelola.

🔗 Baca Juga