We'll combine everything you've learned so far — components, props, state, events, conditional rendering, and list rendering — to build a Mini Todo App. It's a small project, but it'll give your React thinking a really solid workout.
import { useState } from 'react';
function TodoApp() {
const [text, setText] = useState('');
const [todos, setTodos] = useState([]);
function addTodo(event) {
event.preventDefault();
const cleanText = text.trim();
if (!cleanText) return;
const newTodo = {
id: Date.now(),
text: cleanText
};
setTodos([...todos, newTodo]);
setText('');
}
return (
<main>
<h1>React Todo Mini Project</h1>
<form onSubmit={addTodo}>
<input
value={text}
onChange={event => setText(event.target.value)}
placeholder="Write a task..."
/>
<button type="submit">Add</button>
</form>
{todos.length === 0 ? (
<p>No tasks yet. Add your first task.</p>
) : (
<ul>
{todos.map(todo => (
<li key={todo.id}>{todo.text}</li>
))}
</ul>
)}
</main>
);
}
export default TodoApp;The `text` state controls the input value, and the `todos` state controls the task list. On submit, we trim whitespace and only add the task to the array if it's non-empty. Once it's added, we clear the input.
Every time you type a task and hit Add, a new task gets added to the list. If there are no tasks yet, an empty message shows up.Info
Notice that when updating the array state, we don't use `todos.push()` — instead we build a brand-new array with `setTodos([...todos, newTodo])`. Treating React state with an immutable mindset matters a lot.
Summary
This mini project shows off an important flow in any React app: capturing user input into state, updating it with events, rendering an array as a list UI, and showing an empty state based on a condition.