Lanjutkan proyek To-Do List React+TS! Bangun komponen `TodoList.tsx` untuk merender daftar tugas dan `TodoItem.tsx` untuk menampilkan setiap tugas individual, lengkap dengan props yang diketik.
17m bacaanKuis WajibMicro Badge Lesson
Proyek To-Do List (React + TS) #3: Nampilin Daftar Tugasnya Biar Kece!
Udah bisa nambahin tugas baru ke state todos di App.tsx lewat TodoForm? Mantap! Sekarang, tugas-tugas yang udah disimpen itu mau kita tampilin dong di halaman biar keliatan.
Kita bakal bikin dua komponen baru buat ini:
TodoItem.tsx: Komponen kecil yang tugasnya nampilin satu item tugas individual. Dia bakal nerima data satu tugas (ID, teks, status selesai) sebagai props.
TodoList.tsx: Komponen yang tugasnya ngerender keseluruhan daftar tugas. Dia bakal nerima array todos dari App.tsx sebagai prop, terus nge-map array itu jadi sekumpulan komponen TodoItem.
Ini contoh bagus buat liat gimana kita mecah UI jadi komponen yang lebih kecil dan reusable, dan gimana data (props) ngalir dari parent ke child.
Langkah 1: Membuat Komponen TodoItem.tsx
Komponen ini yang paling spesifik, ngurusin tampilan satu biji tugas.
Di folder , bikin file baru .
src/components/
TodoItem.tsx
Isi kodenya:
File src/components/TodoItem.tsx:
tsx
import React from 'react';import { Todo } from '../App'; // Impor interface Todo dari App.tsx (atau types.ts)// Definisikan tipe untuk props yang diterima TodoIteminterface TodoItemProps { todo: Todo; // Satu objek todo // Fungsi-fungsi handler ini akan dikirim dari App.tsx nanti onToggleComplete: (id: number) => void; onDeleteTodo: (id: number) => void;
Bedah Kode TodoItem.tsx:
Impor Todo: Kita impor interface Todo yang udah kita bikin di App.tsx (atau nanti bisa dipindah ke file types.ts sendiri biar lebih rapi). Ini penting biar TodoItemProps bisa pake tipe Todo.
Kalau App.tsx dan TodoItem.tsx ada di folder yang beda (TodoItem di components/), path impornya jadi import { Todo } from '../App'; (naik satu level dari components ke src, baru ke App).
todos.map(todo => ...): Ini jurus andalan buat ngerender list di React! Kita nge-loop tiap todo di array todos.
Buat tiap todo, kita nge-return satu komponen <TodoItem />.
key={todo.id}: Ini SUPER PENTING. React butuh key unik buat tiap item di list biar dia bisa ngelola update DOM dengan efisien. Kita pake todo.id yang udah kita pastiin unik.
todo={todo}: Kita ngirim seluruh objek todo saat ini sebagai prop ke TodoItem.
onToggleComplete={onToggleComplete} dan onDeleteTodo={onDeleteTodo}: Kita "nerusin" fungsi handler dari App.tsx (yang diterima TodoList sebagai prop) ke tiap TodoItem sebagai prop juga. Jadi, pas tombol di TodoItem diklik, dia sebenernya manggil fungsi yang ada di App.tsx.
Langkah 3: Menggunakan TodoList di App.tsx
Sekarang, kita update lagi src/App.tsx buat ngimpor dan ngerender TodoList, dan ngimplementasiin fungsi toggleComplete serta deleteTodo.
File src/App.tsx (bagian yang relevan diubah/ditambah):
tsx
import React, { useState, useEffect } from 'react';import './App.css';import TodoForm from './components/TodoForm';import TodoList from './components/TodoList'; // <-- IMPORT TodoListexport interface Todo { id: number; text: string; isCompleted: boolean;}function App() { const [todos, setTodos] = useState<Todo[]>(() => { const savedTodos = localStorage.getItem('todos'); if (savedTodos) { try { return JSON.parse(savedTodos) as Todo[]; } catch (e) { console.error("Gagal mem-parse todos dari localStorage:", e); return []; } } return []; }); useEffect(() => { localStorage.setItem('todos', JSON.stringify(todos)); }, [todos]); const addTodo = (textDariForm: string) => { if (!textDariForm.trim()) return; const newTodo: Todo = { id: Date.now(), text: textDariForm, isCompleted: false, }; setTodos(prevTodos => [...prevTodos, newTodo]); }; // Fungsi buat toggle status selesai/belum tugas const toggleComplete = (idToToggle: number) => { // Kasih tipe ke parameter id setTodos(prevTodos => prevTodos.map(todo => todo.id === idToToggle ? { ...todo, isCompleted: !todo.isCompleted } : todo ) ); }; // Fungsi buat ngehapus tugas const deleteTodo = (idToDelete: number) => { // Kasih tipe ke parameter id 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} /> {/* Render TodoList dan kirim props yang dibutuhkan */} <TodoList todos={todos} onToggleComplete={toggleComplete} onDeleteTodo={deleteTodo} /> </main> <footer className="app-footer"> {/* Tambah class untuk styling footer */} <p>Total Tugas: {todos.length} | Selesai: {todos.filter(t => t.isCompleted).length}</p> </footer> </div> );}export default App;
Perubahan Penting di App.tsx:
Impor TodoList.
Implementasi toggleComplete(idToToggle: number):
Pake setTodos dengan fungsi updater.
Dia nge-map array todos lama. Kalau todo.id cocok sama idToToggle, dia bikin objek todo baru dengan semua properti lama (...todo) tapi isCompleted-nya dibalik (!todo.isCompleted). Kalau gak cocok, dia balikin objek todo yang lama.
Implementasi deleteTodo(idToDelete: number):
Pake setTodos dengan fungsi updater.
Dia nge-filter array todos lama, cuma nyimpen todo yang id-nya GAK SAMA dengan idToDelete.
Render TodoList:
<TodoList todos={todos} onToggleComplete={toggleComplete} onDeleteTodo={deleteTodo} />: Kita kirim state todos dan dua fungsi handler baru ini sebagai props ke TodoList.
Langkah 4: Styling Tambahan (Opsional)
Kamu bisa nambahin style lagi ke src/App.css buat class .todo-list, .todo-item, .completed-text, .empty-message, dan .app-footer biar tampilannya makin oke. (Contoh stylingnya udah ada di TodoItem.jsx pake inline style, dan di style-todo.css dari materi Proyek Mini JS sebelumnya, bisa kamu adaptasi).
Coba Jalanin Lagi!
Kalau semua udah bener:
Pastikan dev server Vite (npm run dev) masih jalan.
Refresh browser.
Sekarang, kamu harusnya udah bisa:
Nambahin tugas baru.
Liat daftar tugasnya muncul.
Ngeklik teks tugas atau tombol "Selesai"/"Batal" buat nandain tugas (teksnya jadi dicoret).
Ngeklik tombol "Hapus" buat ngilangin tugas dari daftar.
Semua perubahan juga bakal kesimpen di localStorage!
Hore! Aplikasi To-Do List React + TypeScript kita udah punya fungsionalitas inti! Di sini kita liat gimana:
Komponen dipecah jadi bagian-bagian yang lebih kecil dan fokus (App -> TodoList -> TodoItem).
Props dipake buat ngalirin data (todos) dan fungsi callback (onToggleComplete, onDeleteTodo) dari parent ke child, sampe ke child paling dalem.
State utama (todos) tetep dipegang sama komponen parent bersama terdekat (App) - ini contoh nyata Lifting State Up.