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
, dandeleteTodo
sebagai props dariApp
. - Nge-map array
todos
buat ngerender banyak komponenTodoItem
. - Nerusin lagi prop
todo
(satu item tugas),onToggleComplete
(yang aslinyatoggleComplete
dariApp
), danonDeleteTodo
(yang aslinyadeleteTodo
dariApp
) ke tiapTodoItem
.
- Nerima
TodoItem.tsx
(Child dariTodoList
):- Nerima
todo
(satu objek tugas),onToggleComplete
, danonDeleteTodo
sebagai 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
setTodos
dengan fungsi updater (prevTodos => ...
). Ini praktik baik kalau state baru bergantung sama state lama.prevTodos
adalah nilaitodos
sebelum update ini. - Kita nge-
map
arrayprevTodos
. Buat tiaptodo
:- Kalau
todo.id
sama denganidToToggle
, kita bikin objektodo
baru pake spread operator (...todo
) buat nyalin semua properti lama, TAPI kita override propertiisCompleted
jadi kebalikannya (!todo.isCompleted
). - Kalau
id
-nya gak cocok, kita balikin aja objektodo
itu apa adanya.
- Kalau
- Hasil dari
.map()
adalah arraytodos
baru 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
idToToggle
itunumber
. - Pas kita bikin objek todo baru
{ ...todo, isCompleted: !todo.isCompleted }
, TypeScript (kalauinterface Todo
didefinisiin 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
setTodos
dengan fungsi updater. - Kita pake metode array
.filter()
. Metode ini bakal bikin array baru yang isinya cuma elemen-elemen dari arrayprevTodos
yang "lulus tes". - Tesnya adalah
todo => todo.id !== idToDelete
. Artinya, cumatodo
yang ID-nya TIDAK SAMA denganidToDelete
yang bakal dimasukin ke array baru. Jadi,todo
yang mau dihapus otomatis gak ikut lagi. - Hasil dari
.filter()
adalah arraytodos
baru yang udah gak ada tugas yang dihapus, yang kemudian jadi nilai baru buat statetodos
.
Peran TypeScript di sini:
- Mastiin
idToDelete
itunumber
. - Fungsi callback di
.filter()
(todo => ...
) juga dapet manfaat dari tipetodo
yang 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.id
sebagai argumen keonToggleComplete
pas 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
onClick
di tombol itu manggil arrow function() => onDeleteTodo(todo.id)
. - Arrow function itu ngejalanin
onDeleteTodo(todo.id)
(yang sebenernya adalah fungsideleteTodo
dariApp.tsx
yang udah dioper lewat props).todo.id
yang dikirim adalah ID dari item spesifik itu. - Fungsi
deleteTodo
diApp.tsx
dijalanin denganid
yang bener. deleteTodo
ngupdate statetodos
(dengan nge-filter).App.tsx
(dan semua anaknya yang relevan, termasukTodoList
danTodoItem
yang 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.
Kuis Fungsionalitas Toggle & Delete To-Do (React+TS)
Pertanyaan 1 dari 4
Di komponen `App.tsx`, fungsi `toggleComplete(idToToggle: number)` mengubah state `todos` dengan cara...