K

Command Palette

Search for a command to run...

Daftar

Studi Kasus To-Do: localStorage & Penyempurnaan

Finalisasi aplikasi To-Do List React+TS! Pelajari cara menyimpan daftar tugas ke localStorage agar tidak hilang saat refresh, dan beberapa ide penyempurnaan styling atau fungsionalitas.

Proyek To-Do List (React + TS) #5: Biar Gak Ilang & Makin Kinclong!

Aplikasi To-Do List kita udah makin canggih nih! Udah bisa nambah, nandain selesai, dan ngehapus tugas. Tapi, ada satu masalah: setiap kali kita nge-refresh halaman browser, semua daftar tugasnya ilang! Sayang banget kan kalau kerjaan kita gak kesimpen.

Nah, di bagian terakhir studi kasus ini, kita bakal ngatasin masalah itu dengan cara nyimpen daftar tugas kita ke localStorage browser. Jadi, meskipun halaman di-refresh atau browsernya ditutup terus dibuka lagi, daftar tugas kita bakal tetep ada.

Selain itu, kita juga bisa mikirin beberapa sentuhan akhir buat bikin aplikasi kita makin enak diliat dan dipake.

Menggunakan localStorage untuk Persistensi Data Tugas

Kamu pasti inget kan materi soal Web Storage (localStorage)? localStorage ini cocok banget buat nyimpen data sederhana kayak daftar tugas kita.

Kita udah sempet nambahin logikanya di App.tsx pas pertama kali ngeset state todos dan pake useEffect. Yuk, kita bedah lagi biar makin paham.

Review Kode di src/App.tsx:

tsx
// src/App.tsx
import React, { useState, useEffect } from 'react';
import './App.css';
import TodoForm from './components/TodoForm';
import TodoList from './components/TodoList';
 
export interface Todo {
  id: number;
  text: string;
  isCompleted: boolean;
}
 
function App() {
  // 1. Mengambil data dari localStorage saat komponen pertama kali dimuat
  const [todos, setTodos] = useState<Todo[]>(() => { 
    const savedTodos = localStorage.getItem('todos'); // Ambil string dari localStorage
    if (savedTodos) {
      try {
        // Ubah string JSON kembali jadi array objek Todo
        return JSON.parse(savedTodos) as Todo[]; // Type assertion
      } catch (e) {
        console.error("Gagal mem-parse todos dari localStorage:", e);
        return []; // Kembalikan array kosong jika parsing gagal
      }
    }
    return []; // Default array kosong jika tidak ada data tersimpan
  });
 
  // 2. Menggunakan useEffect untuk MENYIMPAN 'todos' ke localStorage
  //    SETIAP KALI state 'todos' berubah.
  useEffect(() => {
    // localStorage hanya bisa menyimpan string, 
    // jadi kita ubah array 'todos' jadi string JSON.
    localStorage.setItem('todos', JSON.stringify(todos));
    console.log("Todos disimpan ke localStorage:", todos);
  }, [todos]); // Array dependensi [todos] bikin efek ini jalan tiap kali 'todos' berubah.
 
  // ... (fungsi addTodo, toggleComplete, deleteTodo tetap sama) ...
  const addTodo = (textDariForm: string) => {
    if (!textDariForm.trim()) return;
    const newTodo: Todo = { id: Date.now(), text: textDariForm, isCompleted: false };
    setTodos(prevTodos => [...prevTodos, newTodo]);
  };
 
  const toggleComplete = (idToToggle: number) => {
    setTodos(prevTodos =>
      prevTodos.map(todo =>
        todo.id === idToToggle ? { ...todo, isCompleted: !todo.isCompleted } : todo
      )
    );
  };
 
  const deleteTodo = (idToDelete: number) => {
    setTodos(prevTodos => prevTodos.filter(todo => todo.id !== idToDelete));
  };
 
 
  return (
    <div className="app-container">
      <header><h1>Aplikasi To-Do List Saya (React + TS)</h1></header>
      <main>
        <TodoForm onAddTodo={addTodo} />
        <TodoList 
          todos={todos} 
          onToggleComplete={toggleComplete}
          onDeleteTodo={deleteTodo}
        />
      </main>
      <footer className="app-footer">
        <p>Total Tugas: {todos.length} | Selesai: {todos.filter(t => t.isCompleted).length}</p>
      </footer>
    </div>
  );
}
 
export default App;

Penjelasan Logika localStorage:

  1. Mengambil Data Saat Load (di useState Initializer Function):

    • Pas useState<Todo[]>(...) dipanggil pertama kali, kita ngasih dia fungsi initializer (() => { ... }). Fungsi ini cuma dijalanin sekali pas komponen App pertama kali di-mount.
    • Di dalem fungsi itu:
      • localStorage.getItem('todos'): Kita coba ambil data dengan kunci 'todos' dari localStorage. Hasilnya bakal berupa string (kalau ada) atau null (kalau gak ada).
      • if (savedTodos): Kalau ada data tersimpan:
        • JSON.parse(savedTodos): Kita ubah string JSON itu balik jadi array objek JavaScript.
        • as Todo[]: Kita kasih tau TypeScript kalau kita yakin hasil parse ini adalah array yang isinya objek-objek Todo.
        • Blok try...catch dipake buat jaga-jaga kalau data di localStorage ternyata rusak atau bukan JSON valid, biar aplikasi gak crash.
      • Kalau gak ada data tersimpan (atau parsing gagal), kita balikin array kosong [] sebagai nilai awal state todos.
  2. Menyimpan Data Setiap Kali todos Berubah (di useEffect):

    • Kita pake useEffect buat ngejalanin "efek samping" setiap kali ada perubahan di state todos.
    • Fungsi efek di dalem useEffect ini simpel: localStorage.setItem('todos', JSON.stringify(todos));.
      • JSON.stringify(todos): Kita ubah array objek todos jadi string JSON sebelum disimpen, karena localStorage cuma bisa nyimpen string.
    • [todos] di array dependensi useEffect itu penting banget! Artinya, fungsi efek ini (yang nyimpen ke localStorage) cuma bakal dijalanin ulang kalau nilai state todos berubah. Jadi, setiap kali kita nambah, ngubah status, atau ngehapus todo, data terbaru bakal otomatis kesimpen.

Coba Sekarang! Kalau kamu udah ngikutin dari awal, coba tambahin beberapa tugas, terus refresh halaman browser-mu. Harusnya daftar tugasmu gak ilang! Ajaib kan? Itu karena pas halaman di-load ulang, useState ngambil lagi data terakhir yang kesimpen di localStorage.

Kamu juga bisa buka Browser DevTools > Tab Application > Local Storage buat liat data todos kamu kesimpen di situ sebagai string JSON.

Ide Penyempurnaan Tambahan (Kalau Kamu Tertantang!)

Aplikasi To-Do List kita udah fungsional, tapi selalu ada ruang buat improvisasi. Ini beberapa ide (gak kita implementasiin semua di sini ya, tapi bisa jadi latihan buatmu!):

  1. Styling yang Lebih Ciamik:

    • File App.css kita masih kosong. Kamu bisa nambahin style yang lebih bagus buat kontainer, judul, form, daftar tugas, dan item tugasnya.
    • Mungkin pake Flexbox atau Grid buat layout yang lebih kompleks.
    • Gimana kalau tombol "Selesai" dan "Hapus" cuma muncul pas item tugasnya di-hover? (Pake CSS :hover di parent dan ngubah display atau opacity tombol).
    • Atau, kamu bisa coba ngaplikasiin Tailwind CSS yang udah kita pelajari buat nge-style seluruh aplikasi ini! Itu bakal jadi latihan yang bagus banget.
  2. Tombol "Bersihkan Semua Tugas Selesai":

    • Bikin tombol baru di App.tsx yang pas diklik bakal ngehapus semua tugas yang isCompleted-nya true.
  3. Edit Teks Tugas:

    • Ini agak lebih kompleks. Kamu perlu nambahin tombol "Edit" di TodoItem. Pas diklik, teks tugasnya jadi input field yang bisa diedit. Pas selesai edit, panggil fungsi di App.tsx buat ngupdate teks tugas yang ID-nya sesuai.
  4. Filter Tugas (Semua, Aktif, Selesai):

    • Bikin tombol-tombol filter. State baru di App.tsx buat nyimpen filter yang lagi aktif.
    • TodoList cuma ngerender tugas yang sesuai sama filter aktif.
  5. Animasi atau Transisi:

    • Kasih efek transisi pas nambah atau ngehapus tugas biar lebih smooth. (Bisa pake library kayak react-transition-group atau trik CSS).
  6. Validasi Input yang Lebih Baik:

    • Selain ngecek kosong, mungkin ada validasi lain (misal, panjang maksimal teks tugas).

Ini cuma beberapa ide. Dunia pengembangan itu soal terus belajar dan ningkatin apa yang udah kita buat!


Selamat! Kamu udah berhasil ngebangun aplikasi To-Do List yang lumayan lengkap pake React dan TypeScript, dari nol sampe bisa nyimpen data! Ini adalah pencapaian besar dan nunjukin kalau kamu udah ngertiin banyak banget konsep inti React.

Studi kasus ini (dan proyek-proyek lain yang bakal kamu bikin) adalah cara terbaik buat ngasah skill codingmu. Jangan pernah bosen buat ngoprek, nyoba hal baru, dan yang paling penting, nikmatin proses belajarnya!

Semoga panduan ReactJS ini bermanfaat buatmu!


Kuis Penyempurnaan & localStorage To-Do List (React+TS)

Pertanyaan 1 dari 4

Mengapa kita menggunakan `JSON.stringify()` sebelum menyimpan array `todos` ke `localStorage`?