Studi Kasus To-Do: Fungsi Toggle & Delete
Lengkapi fungsionalitas aplikasi To-Do List React+TS Anda! Pahami alur kerja bagaimana aksi 'toggle selesai' dan 'hapus' pada item tugas memicu update state di komponen utama.
Proyek To-Do List (React + TS) #4: Bikin Tugas Bisa "Dicoret" dan "Dibuang"!
Aplikasi To-Do List kita udah bisa nambahin tugas baru dan nampilin daftarnya. Keren! Tapi, belum lengkap rasanya kalau kita gak bisa:
- Nandain tugas mana yang udah selesai (biasanya dicoret).
- Ngehapus tugas yang udah gak relevan lagi.
Di bagian ini, kita bakal fokus buat ngimplementasiin dua fungsionalitas inti itu. Kita udah sempet bikin kerangka fungsinya (toggleComplete dan deleteTodo) di App.tsx dan udah ngoper mereka sebagai props sampe ke TodoItem.tsx. Sekarang, mari kita pastikan kita bener-bener ngerti alur kerjanya dan gimana TypeScript ngebantu di sini.
Review Singkat: Alur Data dan Fungsi dari App ke TodoItem
Ingat lagi ya, di React, data itu umumnya ngalir dari atas ke bawah (parent ke child) lewat props. Buat ngubah data yang ada di parent (state), child biasanya manggil fungsi callback yang dikasih sama parent sebagai prop.
Di aplikasi To-Do kita:
App.tsx(Parent Tertinggi):- Nyimpen state utama:
const [todos, setTodos] = useState<Todo[]>(...);. - Ngedefinisiin fungsi-fungsi buat ngubah state itu:
addTodo,toggleComplete,deleteTodo.
- Nyimpen state utama:
TodoList.tsx(Child dariApp, Parent dariTodoItem):- Nerima
todos,toggleComplete, dandeleteTodosebagai props dariApp. - Nge-map array
todosbuat ngerender banyak komponenTodoItem. - Nerusin lagi prop
todo(satu item tugas),onToggleComplete(yang aslinyatoggleCompletedariApp), danonDeleteTodo(yang aslinyadeleteTododariApp) ke tiapTodoItem.
- Nerima
TodoItem.tsx(Child dariTodoList):- Nerima
todo(satu objek tugas),onToggleComplete, danonDeleteTodosebagai props. - Pas tombol "Selesai/Batal" atau "Hapus" diklik, dia manggil fungsi prop
onToggleComplete(todo.id)atauonDeleteTodo(todo.id).
- Nerima
Ini adalah contoh klasik dari Lifting State Up dan Passing Functions as Props.
Implementasi Fungsi toggleComplete di App.tsx (Sudah Kita Buat)
Yuk, kita liat lagi kode toggleComplete di App.tsx dan pastikan kita paham:
// src/App.tsx
// ... (import dan interface Todo) ...
function App() {
// ... (state todos dan useEffect localStorage) ...
// ... (fungsi addTodo) ...
const toggleComplete = (idToToggle: number) => { // Parameter 'idToToggle' tipenya number
setTodos(prevTodos => // Pake fungsi updater buat mastiin kita kerja dengan state terbaru
prevTodos.map(todo => // Bikin array baru pake .map() (prinsip immutability)
todo.id === idToToggle // Kalau ID todo saat ini cocok sama ID yang mau di-toggle...
? { ...todo, isCompleted: !todo.isCompleted } // ...bikin objek todo baru dengan isCompleted dibalik
: todo // Kalau gak cocok, balikin objek todo apa adanya
)
);
console.log(`Status completed untuk todo ID ${idToToggle} di-toggle.`);
};
// ... (fungsi deleteTodo dan return JSX) ...
}Penjelasan toggleComplete:
- Fungsi ini nerima
idToToggle(nomor ID tugas yang mau diubah statusnya). TypeScript mastiin ininumber. - Kita pake
setTodosdengan fungsi updater (prevTodos => ...). Ini praktik baik kalau state baru bergantung sama state lama.prevTodosadalah nilaitodossebelum update ini. - Kita nge-
maparrayprevTodos. Buat tiaptodo:- Kalau
todo.idsama denganidToToggle, kita bikin objektodobaru pake spread operator (...todo) buat nyalin semua properti lama, TAPI kita override propertiisCompletedjadi kebalikannya (!todo.isCompleted). - Kalau
id-nya gak cocok, kita balikin aja objektodoitu apa adanya.
- Kalau
- Hasil dari
.map()adalah arraytodosbaru yang udah diupdate, yang kemudian jadi nilai baru buat statetodos. React bakal otomatis nge-re-render komponen yang make state ini.
Peran TypeScript di sini:
- Mastiin
idToToggleitunumber. - Pas kita bikin objek todo baru
{ ...todo, isCompleted: !todo.isCompleted }, TypeScript (kalauinterface Tododidefinisiin dengan benar) bakal ngecek apakah struktur objek baru ini masih sesuai samainterface Todo.
Implementasi Fungsi deleteTodo di App.tsx (Sudah Kita Buat)
Sekarang kita liat lagi deleteTodo:
// src/App.tsx
// ... (kode sebelumnya) ...
const deleteTodo = (idToDelete: number) => { // Parameter 'idToDelete' tipenya number
setTodos(prevTodos => // Pake fungsi updater
prevTodos.filter(todo => todo.id !== idToDelete) // Bikin array baru pake .filter()
);
console.log(`Todo dengan ID ${idToDelete} dihapus.`);
};
return (
// ... (return JSX dengan TodoList yang nerima toggleComplete dan deleteTodo) ...
// ...Penjelasan deleteTodo:
- Fungsi ini nerima
idToDelete(nomor ID tugas yang mau dihapus). - Kita pake
setTodosdengan fungsi updater. - Kita pake metode array
.filter(). Metode ini bakal bikin array baru yang isinya cuma elemen-elemen dari arrayprevTodosyang "lulus tes". - Tesnya adalah
todo => todo.id !== idToDelete. Artinya, cumatodoyang ID-nya TIDAK SAMA denganidToDeleteyang bakal dimasukin ke array baru. Jadi,todoyang mau dihapus otomatis gak ikut lagi. - Hasil dari
.filter()adalah arraytodosbaru yang udah gak ada tugas yang dihapus, yang kemudian jadi nilai baru buat statetodos.
Peran TypeScript di sini:
- Mastiin
idToDeleteitunumber. - Fungsi callback di
.filter()(todo => ...) juga dapet manfaat dari tipetodoyang udah diketahui (yaituTodo) dari arrayprevTodos(yang tipenyaTodo[]).
Cara TodoItem.tsx Memicu Fungsi-Fungsi Ini
Di materi sebelumnya, kita udah bikin TodoItem.tsx yang punya tombol "Selesai/Batal" dan "Hapus". Mari kita liat lagi gimana dia manggil fungsi prop dari App.tsx:
File src/components/TodoItem.tsx (bagian relevan):
// ... (import dan interface TodoItemProps) ...
const TodoItem: React.FC<TodoItemProps> = ({ todo, onToggleComplete, onDeleteTodo }) => {
// ... (definisi style) ...
return (
<li /* ... */>
<span onClick={() => onToggleComplete(todo.id)} /* ... */>
{todo.text}
</span>
<div>
<button onClick={() => onToggleComplete(todo.id)} /* ... */ >
{todo.isCompleted ? 'Batal' : 'Selesai'}
</button>
<button onClick={() => onDeleteTodo(todo.id)} /* ... */ >
Hapus
</button>
</div>
</li>
);
};- Perhatiin pas
onClick:onClick={() => onToggleComplete(todo.id)}: Kita pake arrow function pembungkus biar bisa ngirimtodo.idsebagai argumen keonToggleCompletepas event terjadi. Kalau kita tulisonClick={onToggleComplete(todo.id)}(tanpa arrow function), fungsinya bakal langsung dijalanin pas render, bukan pas diklik!- Hal yang sama berlaku buat
onDeleteTodo(todo.id).
Alur Lengkapnya (misal, buat Hapus Tugas):
- Pengguna ngeklik tombol "Hapus" di salah satu
TodoItem. - Event
onClickdi tombol itu manggil arrow function() => onDeleteTodo(todo.id). - Arrow function itu ngejalanin
onDeleteTodo(todo.id)(yang sebenernya adalah fungsideleteTododariApp.tsxyang udah dioper lewat props).todo.idyang dikirim adalah ID dari item spesifik itu. - Fungsi
deleteTododiApp.tsxdijalanin denganidyang bener. deleteTodongupdate statetodos(dengan nge-filter).App.tsx(dan semua anaknya yang relevan, termasukTodoListdanTodoItemyang tersisa) bakal di-re-render sama React buat nampilin daftar tugas yang udah baru.- Voila! Tugasnya ilang dari layar!
Styling Kondisional di TodoItem.tsx (Review)
Inget juga di TodoItem.tsx, kita udah nerapin conditional styling buat teks tugas yang selesai:
// src/components/TodoItem.tsx
// ...
const itemStyle: React.CSSProperties = {
textDecoration: todo.isCompleted ? 'line-through' : 'none', // INI DIA!
color: todo.isCompleted ? '#a0a0a0' : '#333',
opacity: todo.isCompleted ? 0.6 : 1,
// ...
};
// ...
<span style={{ textDecoration: todo.isCompleted ? 'line-through' : 'none' }} ... >
{todo.text}
</span>
// ...Atau kalau pake class CSS di App.css (lebih disarankan):
/* src/App.css */
.todo-item.completed .todo-text { /* Misal, teks tugas ada di dalem <span class="todo-text"> */
text-decoration: line-through;
color: #aaa;
opacity: 0.6;
}// src/components/TodoItem.tsx
// ...
return (
<li className={`todo-item ${todo.isCompleted ? 'completed' : ''}`}>
<span className="todo-text" onClick={() => onToggleComplete(todo.id)}>
{todo.text}
</span>
{/* ... tombol ... */}
</li>
);Pas todo.isCompleted jadi true (setelah toggleComplete dipanggil), TodoItem bakal di-re-render, dan style "coret" bakal diterapin.
Dengan fungsionalitas toggle dan delete ini, aplikasi To-Do List kita jadi makin lengkap! Kita udah liat gimana alur data dan fungsi callback bekerja dari parent sampe ke child paling dalem, dan gimana TypeScript ngebantu mastiin semuanya nyambung dengan tipe yang bener.
Ini adalah pola yang bakal sering banget kamu pake di aplikasi React yang lebih kompleks. Pahami baik-baik ya alur "angkat state" dan "oper fungsi"-nya!
Di bagian terakhir studi kasus ini, kita bakal nambahin sentuhan akhir, yaitu nyimpen data tugas kita ke localStorage biar gak ilang pas halaman di-refresh, dan mungkin sedikit penyempurnaan styling.
Uji Pemahamanmu!
Memeriksa status login...
Sebelumnya
Studi Kasus To-Do: Komponen List & Item
Selanjutnya
Studi Kasus To-Do: localStorage & Penyempurnaan