K

Command Palette

Search for a command to run...

Daftar

Proyek Mini ReactJS

Waktunya ngoding proyek React pertamamu! Aplikasikan semua konsep seperti komponen, props, state, event handling, dan list rendering untuk membangun aplikasi To-Do List interaktif dari awal.

Bikin Karya Nyata: Bangun Aplikasi To-Do List Keren Pake React!

Teori udah, konsep udah, sekarang saatnya PRAKTIK! Cara terbaik buat bener-bener ngeresapin semua yang udah kita pelajari soal ReactJS adalah dengan langsung ngebangun sesuatu. Di bagian ini, kita bakal ngebikin Aplikasi To-Do List sederhana tapi fungsional pake React.

Fitur yang Mau Kita Bikin:

  1. Nampilin daftar tugas.
  2. Form buat nambahin tugas baru.
  3. Kemampuan buat nandain tugas sebagai "selesai" (misal, teksnya dicoret).
  4. Kemampuan buat ngehapus tugas.

Ini bakal ngelibatin hampir semua konsep inti React yang udah kita bahas: komponen, JSX, props, state (useState), event handling, conditional rendering, dan list rendering (pake .map() dan key).

Siapin VS Code-mu, pastiin proyek Vite + React-mu udah siap (atau bikin baru kalau mau!), dan yuk kita mulai ngoding!

Langkah 0: Struktur Awal Proyek

Anggap kamu udah punya proyek React yang dibuat pake Vite (misalnya, npm create vite@latest my-todo-app -- --template react). Kita bakal banyak kerja di folder src/.

Kita bisa mulai dengan ngebersihin file src/App.jsx dan src/App.css dari konten default Vite.

Langkah 1: Komponen Utama Aplikasi (App.jsx)

Komponen App ini bakal jadi "otak" utama yang nyimpen daftar tugas (state) dan ngatur logika dasarnya.

Disini kita bakal ngebuat state todos yang bakal nyimpen daftar tugas di local storage (Simak ulang cara nyimpen data di Browser (localStorage) ). Kita juga bakal ngebuat fungsi-fungsi handler seperti addTodo, toggleComplete, dan deleteTodo untuk ngubah state tersebut.

File src/App.jsx:

jsx
import React, { useState, useEffect } from 'react';
import './App.css'; // Kita akan isi style-nya nanti
import TodoForm from './components/TodoForm'; // Komponen buat form input
import TodoList from './components/TodoList'; // Komponen buat nampilin daftar tugas
 
function App() {
  // State buat nyimpen semua tugas. Nilai awalnya array kosong.
  // Tiap tugas bakal jadi objek: { id: ..., text: ..., isCompleted: ... }
  const [todos, setTodos] = useState(() => {
    // Coba ambil todos dari localStorage pas pertama kali load
    const savedTodos = localStorage.getItem('todos');
    return savedTodos ? JSON.parse(savedTodos) : [];
  });
 
  // useEffect buat nyimpen todos ke localStorage setiap kali 'todos' berubah
  useEffect(() => {
    localStorage.setItem('todos', JSON.stringify(todos));
  }, [todos]);
 
  // Fungsi buat nambahin tugas baru
  const addTodo = (text) => {
    if (!text.trim()) return; // Jangan tambah kalau teks kosong atau cuma spasi
    const newTodo = {
      id: Date.now(), // ID unik sederhana pake timestamp
      text: text,
      isCompleted: false,
    };
    setTodos([...todos, newTodo]); // Tambah todo baru ke array todos yang lama
  };
 
  // Fungsi buat toggle status selesai/belum tugas
  const toggleComplete = (id) => {
    setTodos(
      todos.map(todo =>
        todo.id === id ? { ...todo, isCompleted: !todo.isCompleted } : todo
      )
    );
  };
 
  // Fungsi buat ngehapus tugas
  const deleteTodo = (id) => {
    setTodos(todos.filter(todo => todo.id !== id));
  };
 
  return (
    <div className="app-container">
      <header>
        <h1>To-Do List React Saya!</h1>
      </header>
      <main>
        <TodoForm onAddTodo={addTodo} /> {/* Kirim fungsi addTodo sebagai prop */}
        <TodoList 
          todos={todos} 
          onToggleComplete={toggleComplete}
          onDeleteTodo={deleteTodo}
        /> {/* Kirim daftar todos & fungsi-fungsi sebagai props */}
      </main>
      <footer>
        <p>Total Tugas: {todos.length} | Selesai: {todos.filter(t => t.isCompleted).length}</p>
      </footer>
    </div>
  );
}
 
export default App;

Bedah App.jsx:

  • State todos: Pake useState([]) buat nyimpen array of objek tugas. Kita juga nambahin logika buat ngambil data dari localStorage pas pertama kali load dan nyimpennya lagi setiap kali todos berubah pake useEffect. Ini biar daftar tugasnya gak ilang pas halaman di-refresh!
  • addTodo(text): Fungsi buat nambahin objek tugas baru ke state todos. Perhatiin cara pake spread operator (...todos) buat bikin array baru tanpa ngemutasi state lama.
  • toggleComplete(id): Fungsi buat ngubah status isCompleted dari tugas tertentu. Dia nge-map array todos, nyari tugas yang ID-nya cocok, terus nge-return objek tugas baru dengan status isCompleted yang dibalik.
  • deleteTodo(id): Fungsi buat ngehapus tugas. Dia nge-filter array todos buat ngembaliin semua tugas yang ID-nya GAK SAMA dengan ID yang mau dihapus.
  • Return JSX: Ngerender komponen TodoForm dan TodoList (yang bakal kita bikin), sambil ngirim state todos dan fungsi-fungsi handler itu sebagai props. Footer juga nampilin info jumlah tugas.

Langkah 2: Komponen Form Input (TodoForm.jsx)

Komponen ini tugasnya nampilin input field dan tombol buat nambahin tugas baru.

Buat folder baru src/components/ terus bikin file TodoForm.jsx di dalemnya.

File src/components/TodoForm.jsx:

jsx
import React, { useState } from 'react';
 
function TodoForm({ onAddTodo }) { // Nerima prop onAddTodo dari App
  const [inputText, setInputText] = useState('');
 
  const handleSubmit = (event) => {
    event.preventDefault(); // Cegah reload
    if (!inputText.trim()) return; // Validasi input kosong
 
    onAddTodo(inputText); // Panggil fungsi dari App buat nambah todo
    setInputText(''); // Kosongin input field lagi
  };
 
  return (
    <form onSubmit={handleSubmit} className="todo-form">
      <input 
        type="text"
        value={inputText}
        onChange={(e) => setInputText(e.target.value)}
        placeholder="Ketik tugas baru..."
        className="todo-input"
      />
      <button type="submit" className="todo-button">Tambah</button>
    </form>
  );
}
 
export default TodoForm;

Bedah TodoForm.jsx:

  • Dia punya state lokal inputText buat ngontrol nilai input field.
  • Pas form di-submit, dia manggil fungsi onAddTodo (yang sebenernya adalah addTodo dari App.jsx) sambil ngasih teks inputnya. Ini contoh lifting state up via fungsi callback!

Langkah 3: Komponen Daftar Tugas (TodoList.jsx) dan Item Tugas (TodoItem.jsx)

TodoList bakal ngerender semua tugas. Tiap tugasnya bakal dirender sama komponen TodoItem.

File src/components/TodoItem.jsx:

jsx
import React from 'react';
 
function TodoItem({ todo, onToggleComplete, onDeleteTodo }) {
  const itemStyle = {
    textDecoration: todo.isCompleted ? 'line-through' : 'none',
    color: todo.isCompleted ? '#aaa' : '#333',
    display: 'flex',
    justifyContent: 'space-between',
    alignItems: 'center',
    padding: '10px',
    borderBottom: '1px solid #eee'
  };
 
  const buttonStyle = {
    marginLeft: '10px',
    padding: '5px 8px',
    cursor: 'pointer',
    border: 'none',
    borderRadius: '4px'
  };
 
  const completeButtonStyle = {
    ...buttonStyle,
    backgroundColor: todo.isCompleted ? '#d4edda' : '#cce5ff', // Hijau muda kalau selesai, biru muda kalau belum
    color: todo.isCompleted ? '#155724' : '#004085'
  };
 
   const deleteButtonStyle = {
    ...buttonStyle,
    backgroundColor: '#f8d7da',
    color: '#721c24'
  };
 
  return (
    <li style={itemStyle}>
      <span onClick={() => onToggleComplete(todo.id)} style={{ cursor: 'pointer', flexGrow: 1 }}>
        {todo.text}
      </span>
      <div>
        <button 
          onClick={() => onToggleComplete(todo.id)} 
          style={completeButtonStyle}
        >
          {todo.isCompleted ? 'Batal' : 'Selesai'}
        </button>
        <button 
          onClick={() => onDeleteTodo(todo.id)} 
          style={deleteButtonStyle}
        >
          Hapus
        </button>
      </div>
    </li>
  );
}
 
export default TodoItem;

Bedah TodoItem.jsx:

  • Nerima satu objek todo dan dua fungsi handler (onToggleComplete, onDeleteTodo) sebagai props.
  • Nampilin teks tugas.
  • Style-nya (dicoret atau enggak) tergantung todo.isCompleted (conditional styling).
  • Punya tombol "Selesai/Batal" yang manggil onToggleComplete dengan todo.id.
  • Punya tombol "Hapus" yang manggil onDeleteTodo dengan todo.id.
  • (Styling di sini pake inline style objek biar simpel buat contoh, tapi di proyek beneran mending pake CSS Modules atau Tailwind).

File src/components/TodoList.jsx:

jsx
import React from 'react';
import TodoItem from './TodoItem';
 
function TodoList({ todos, onToggleComplete, onDeleteTodo }) {
  if (todos.length === 0) {
    return <p className="empty-message">Belum ada tugas nih, santai dulu!</p>;
  }
 
  return (
    <ul className="todo-list">
      {todos.map(todo => (
        <TodoItem 
          key={todo.id} // Key wajib buat list!
          todo={todo} 
          onToggleComplete={onToggleComplete}
          onDeleteTodo={onDeleteTodo}
        />
      ))}
    </ul>
  );
}
 
export default TodoList;

Bedah TodoList.jsx:

  • Nerima array todos dan dua fungsi handler dari App.jsx.
  • Kalau todos-nya kosong, dia nampilin pesan.
  • Kalau ada tugas, dia nge-map array todos jadi sekumpulan komponen <TodoItem />, sambil ngoper data todo dan fungsi handler-nya sebagai props ke tiap TodoItem. Jangan lupa key={todo.id}!

Langkah 4: Styling Global (src/App.css dan src/index.css)

Kita kasih sedikit style biar gak terlalu polos.

File src/index.css (biasanya buat reset atau style global banget):

css
body {
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen,
    Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
  background-color: #f4f7f6;
  margin: 0;
  padding: 20px;
  color: #333;
  display: flex;
  justify-content: center;
}
 
#root {
  width: 100%;
  max-width: 600px; /* Biar container app gak terlalu lebar */
}

File src/App.css (style spesifik buat aplikasi To-Do):

css
.app-container {
  background-color: white;
  padding: 20px;
  border-radius: 8px;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}
 
.app-container header h1 {
  text-align: center;
  color: #2c3e50;
  margin-bottom: 20px;
}
 
.todo-form {
  display: flex;
  margin-bottom: 20px;
}
 
.todo-input {
  flex-grow: 1;
  padding: 10px;
  border: 1px solid #ddd;
  border-radius: 4px 0 0 4px;
  font-size: 1rem;
}
 
.todo-input:focus {
  outline: none;
  border-color: #76d7c4;
}
 
.todo-button {
  padding: 10px 15px;
  background-color: #76d7c4;
  color: white;
  border: none;
  border-radius: 0 4px 4px 0;
  cursor: pointer;
  font-size: 1rem;
}
 
.todo-button:hover {
  background-color: #5fbcac;
}
 
.todo-list {
  list-style-type: none;
  padding: 0;
}
 
/* Style buat li dan tombol di TodoItem.jsx udah pake inline style, */
/* tapi kalau mau, bisa dipindahin ke sini pake class. Misal: */
/*
.todo-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 10px;
  border-bottom: 1px solid #eee;
}
.todo-item.completed span {
  text-decoration: line-through;
  color: #aaa;
}
.todo-item .delete-button {
  background-color: #e74c3c;
  color: white;
  border: none;
  padding: 5px 8px;
  border-radius: 4px;
  cursor: pointer;
  margin-left: 5px;
}
.todo-item .toggle-button {
  background-color: #3498db;
  color: white;
  border: none;
  padding: 5px 8px;
  border-radius: 4px;
  cursor: pointer;
}
*/
 
.empty-message {
  text-align: center;
  color: #777;
  padding: 20px;
}
 
.app-container footer {
  text-align: center;
  margin-top: 30px;
  font-size: 0.9em;
  color: #888;
}

Langkah 5: Jalankan Aplikasimu!

Kalau semua file udah disimpen:

  1. Pastikan kamu ada di folder root proyekmu di terminal.
  2. Jalanin npm run dev (kalau belum jalan).
  3. Buka browser ke alamat http://localhost:5173/ (atau port yang dikasih Vite).

Harusnya kamu udah liat aplikasi To-Do List sederhanamu! Coba tambahin tugas, tandain selesai, hapus tugas. Liat juga localStorage di DevTools browser (Tab Application > Local Storage) buat liat data tugasmu kesimpen di sana.


Proyek mini ini ngasih kamu gambaran gimana caranya:

  • Mecah UI jadi komponen-komponen (App, TodoForm, TodoList, TodoItem).
  • Ngelola state aplikasi (todos di App, inputText di TodoForm).
  • Ngoper data dan fungsi antar komponen pake props.
  • Nanganin event pengguna (submit form, klik tombol).
  • Ngerender list data pake .map() dan pentingnya key.
  • Nerapin conditional styling (buat tugas yang selesai).
  • Nambahin sedikit persistensi data pake localStorage dan useEffect.

Ini adalah fondasi yang kuat banget! Dari sini, kamu bisa coba kembangin lagi fiturnya, misalnya:

  • Edit tugas yang udah ada.
  • Filter tugas (semua, aktif, selesai).
  • Ngasih prioritas ke tugas.
  • Dan banyak lagi!

Selamat udah berhasil ngebikin aplikasi React pertamamu yang beneran fungsional! Ini bukti kalau kamu udah mulai "ngeh" sama cara kerja React. Teruslah berlatih dan bikin proyek-proyek lain!