Реализуй базовый to-do: добавление задачи в состояние, её удаление и отметку «выполнено».
Продуктовый фокус. Контроль задач — ядро любого цифрового продукта. Научись работать со списками в состоянии.
[{ id, title, done }].value из state; добавляй задачу по кнопке.Карточка с вводом, счётчиком задач, списком элементов, отметкой выполнения и удалением.
Пример:

const [todos, setTodos] = useState([]) и const [newTitle, setNewTitle] = useState('').setTodos([...todos, { id: Date.now(), title: trimmed, done: false }]).setTodos(todos.map(t => t.id === id ? { ...t, done: !t.done } : t)).setTodos(todos.filter(t => t.id !== id)).trim() и блокируй добавление пустых задач.import React, { useState } from 'react';
import './styles.css';
export function TodoList() {
const [todos, setTodos] = useState([]);
const [newTitle, setNewTitle] = useState('');
function handleAdd() {
const title = newTitle.trim();
if (!title) return;
setTodos([...todos, { id: Date.now(), title, done: false }]);
setNewTitle('');
}
function handleToggle(id) {
setTodos(todos.map(t => t.id === id ? { ...t, done: !t.done } : t));
}
function handleDelete(id) {
setTodos(todos.filter(t => t.id !== id));
}
return (
<article className="card" data-testid="todo-card">
<header>
<h2 className="title">Sprint Focus: список задач</h2>
<p className="subtitle">Добавляй задачи, отмечай выполненные, удаляй лишнее</p>
</header>
<div className="row">
<input
className="input"
type="text"
placeholder="Новая задача..."
value={newTitle}
onChange={e => setNewTitle(e.target.value)}
aria-label="todo-input"
/>
<button className="button" onClick={handleAdd}>Добавить</button>
</div>
<p className="counter" aria-label="counter">{todos.length}</p>
<div className="list" data-testid="todo-list">
{todos.length === 0 ? (
<p className="empty">Нет задач — добавьте первую</p>
) : (
todos.map((t) => (
<div key={t.id} className={`item ${t.done ? 'done' : ''}`}>
<input
className="checkbox"
type="checkbox"
checked={t.done}
onChange={() => handleToggle(t.id)}
aria-label={`toggle-${t.title}`}
/>
<span className="title-inline">{t.title}</span>
<button className="button" onClick={() => handleDelete(t.id)}>Удалить</button>
</div>
))
)}
</div>
</article>
);
}
export default function App() {
return (
<main className="challenge-container">
<section>
<TodoList />
</section>
</main>
);
}Реализуй базовый to-do: добавление задачи в состояние, её удаление и отметку «выполнено».
Продуктовый фокус. Контроль задач — ядро любого цифрового продукта. Научись работать со списками в состоянии.
[{ id, title, done }].value из state; добавляй задачу по кнопке.Карточка с вводом, счётчиком задач, списком элементов, отметкой выполнения и удалением.
Пример:

const [todos, setTodos] = useState([]) и const [newTitle, setNewTitle] = useState('').setTodos([...todos, { id: Date.now(), title: trimmed, done: false }]).setTodos(todos.map(t => t.id === id ? { ...t, done: !t.done } : t)).setTodos(todos.filter(t => t.id !== id)).trim() и блокируй добавление пустых задач.import React, { useState } from 'react';
import './styles.css';
export function TodoList() {
const [todos, setTodos] = useState([]);
const [newTitle, setNewTitle] = useState('');
function handleAdd() {
const title = newTitle.trim();
if (!title) return;
setTodos([...todos, { id: Date.now(), title, done: false }]);
setNewTitle('');
}
function handleToggle(id) {
setTodos(todos.map(t => t.id === id ? { ...t, done: !t.done } : t));
}
function handleDelete(id) {
setTodos(todos.filter(t => t.id !== id));
}
return (
<article className="card" data-testid="todo-card">
<header>
<h2 className="title">Sprint Focus: список задач</h2>
<p className="subtitle">Добавляй задачи, отмечай выполненные, удаляй лишнее</p>
</header>
<div className="row">
<input
className="input"
type="text"
placeholder="Новая задача..."
value={newTitle}
onChange={e => setNewTitle(e.target.value)}
aria-label="todo-input"
/>
<button className="button" onClick={handleAdd}>Добавить</button>
</div>
<p className="counter" aria-label="counter">{todos.length}</p>
<div className="list" data-testid="todo-list">
{todos.length === 0 ? (
<p className="empty">Нет задач — добавьте первую</p>
) : (
todos.map((t) => (
<div key={t.id} className={`item ${t.done ? 'done' : ''}`}>
<input
className="checkbox"
type="checkbox"
checked={t.done}
onChange={() => handleToggle(t.id)}
aria-label={`toggle-${t.title}`}
/>
<span className="title-inline">{t.title}</span>
<button className="button" onClick={() => handleDelete(t.id)}>Удалить</button>
</div>
))
)}
</div>
</article>
);
}
export default function App() {
return (
<main className="challenge-container">
<section>
<TodoList />
</section>
</main>
);
}Редактор кода намеренно скрыт на мобильном.
Поверь, так лучше: я оберегаю тебя от искушения писать код в неидеальных условиях. Маленький экран и виртуальная клавиатура — не лучшие помощники для программиста.
📖 Сейчас: Изучи задачу, продумай решение. Действуй как стратег.
💻 Потом: Сядь за компьютер, открой сайт и реализуй все идеи с комфортом. Действуй как код-джедай!