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
:
// 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
:
-
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 komponenApp
pertama kali di-mount. - Di dalem fungsi itu:
localStorage.getItem('todos')
: Kita coba ambil data dengan kunci'todos'
darilocalStorage
. Hasilnya bakal berupa string (kalau ada) ataunull
(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-objekTodo
.- Blok
try...catch
dipake buat jaga-jaga kalau data dilocalStorage
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 statetodos
.
- Pas
-
Menyimpan Data Setiap Kali
todos
Berubah (diuseEffect
):- Kita pake
useEffect
buat ngejalanin "efek samping" setiap kali ada perubahan di statetodos
. - Fungsi efek di dalem
useEffect
ini simpel:localStorage.setItem('todos', JSON.stringify(todos));
.JSON.stringify(todos)
: Kita ubah array objektodos
jadi string JSON sebelum disimpen, karenalocalStorage
cuma bisa nyimpen string.
[todos]
di array dependensiuseEffect
itu penting banget! Artinya, fungsi efek ini (yang nyimpen kelocalStorage
) cuma bakal dijalanin ulang kalau nilai statetodos
berubah. Jadi, setiap kali kita nambah, ngubah status, atau ngehapus todo, data terbaru bakal otomatis kesimpen.
- Kita pake
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!):
-
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 ngubahdisplay
atauopacity
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.
- File
-
Tombol "Bersihkan Semua Tugas Selesai":
- Bikin tombol baru di
App.tsx
yang pas diklik bakal ngehapus semua tugas yangisCompleted
-nyatrue
.
- Bikin tombol baru di
-
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 diApp.tsx
buat ngupdate teks tugas yang ID-nya sesuai.
- Ini agak lebih kompleks. Kamu perlu nambahin tombol "Edit" di
-
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.
- Bikin tombol-tombol filter. State baru di
-
Animasi atau Transisi:
- Kasih efek transisi pas nambah atau ngehapus tugas biar lebih smooth. (Bisa pake library kayak
react-transition-group
atau trik CSS).
- Kasih efek transisi pas nambah atau ngehapus tugas biar lebih smooth. (Bisa pake library kayak
-
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`?