K

Command Palette

Search for a command to run...

Daftar

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:

  1. Nandain tugas mana yang udah selesai (biasanya dicoret).
  2. 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.
  • TodoList.tsx (Child dari App, Parent dari TodoItem):
    • Nerima todos, toggleComplete, dan deleteTodo sebagai props dari App.
    • Nge-map array todos buat ngerender banyak komponen TodoItem.
    • Nerusin lagi prop todo (satu item tugas), onToggleComplete (yang aslinya toggleComplete dari App), dan onDeleteTodo (yang aslinya deleteTodo dari App) ke tiap TodoItem.
  • TodoItem.tsx (Child dari TodoList):
    • Nerima todo (satu objek tugas), onToggleComplete, dan onDeleteTodo sebagai props.
    • Pas tombol "Selesai/Batal" atau "Hapus" diklik, dia manggil fungsi prop onToggleComplete(todo.id) atau onDeleteTodo(todo.id).

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:

tsx
// 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:

  1. Fungsi ini nerima idToToggle (nomor ID tugas yang mau diubah statusnya). TypeScript mastiin ini number.
  2. Kita pake setTodos dengan fungsi updater (prevTodos => ...). Ini praktik baik kalau state baru bergantung sama state lama. prevTodos adalah nilai todos sebelum update ini.
  3. Kita nge-map array prevTodos. Buat tiap todo:
    • Kalau todo.id sama dengan idToToggle, kita bikin objek todo baru pake spread operator (...todo) buat nyalin semua properti lama, TAPI kita override properti isCompleted jadi kebalikannya (!todo.isCompleted).
    • Kalau id-nya gak cocok, kita balikin aja objek todo itu apa adanya.
  4. Hasil dari .map() adalah array todos baru yang udah diupdate, yang kemudian jadi nilai baru buat state todos. React bakal otomatis nge-re-render komponen yang make state ini.

Peran TypeScript di sini:

  • Mastiin idToToggle itu number.
  • Pas kita bikin objek todo baru { ...todo, isCompleted: !todo.isCompleted }, TypeScript (kalau interface Todo didefinisiin dengan benar) bakal ngecek apakah struktur objek baru ini masih sesuai sama interface Todo.

Implementasi Fungsi deleteTodo di App.tsx (Sudah Kita Buat)

Sekarang kita liat lagi deleteTodo:

tsx
// 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:

  1. Fungsi ini nerima idToDelete (nomor ID tugas yang mau dihapus).
  2. Kita pake setTodos dengan fungsi updater.
  3. Kita pake metode array .filter(). Metode ini bakal bikin array baru yang isinya cuma elemen-elemen dari array prevTodos yang "lulus tes".
  4. Tesnya adalah todo => todo.id !== idToDelete. Artinya, cuma todo yang ID-nya TIDAK SAMA dengan idToDelete yang bakal dimasukin ke array baru. Jadi, todo yang mau dihapus otomatis gak ikut lagi.
  5. Hasil dari .filter() adalah array todos baru yang udah gak ada tugas yang dihapus, yang kemudian jadi nilai baru buat state todos.

Peran TypeScript di sini:

  • Mastiin idToDelete itu number.
  • Fungsi callback di .filter() (todo => ...) juga dapet manfaat dari tipe todo yang udah diketahui (yaitu Todo) dari array prevTodos (yang tipenya Todo[]).

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):

tsx
// ... (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 ngirim todo.id sebagai argumen ke onToggleComplete pas event terjadi. Kalau kita tulis onClick={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):

  1. Pengguna ngeklik tombol "Hapus" di salah satu TodoItem.
  2. Event onClick di tombol itu manggil arrow function () => onDeleteTodo(todo.id).
  3. Arrow function itu ngejalanin onDeleteTodo(todo.id) (yang sebenernya adalah fungsi deleteTodo dari App.tsx yang udah dioper lewat props). todo.id yang dikirim adalah ID dari item spesifik itu.
  4. Fungsi deleteTodo di App.tsx dijalanin dengan id yang bener.
  5. deleteTodo ngupdate state todos (dengan nge-filter).
  6. App.tsx (dan semua anaknya yang relevan, termasuk TodoList dan TodoItem yang tersisa) bakal di-re-render sama React buat nampilin daftar tugas yang udah baru.
  7. 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:

tsx
// 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):

css
/* 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;
}
tsx
// 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...