Загрузка данных
Привет! Это отличная лабораторная для закрепления работы с состояниями и массивами в React. Так как ты будешь делать это в GitHub Codespaces, я составил для тебя пошаговый план, который проведет тебя от настройки окружения до готового пулл-реквеста.
Вот подробная инструкция, как выполнить эту работу.
Шаг 1. Подготовка репозитория и окружения
Согласно твоим требованиям к сдаче работы, сначала нужно правильно подготовить Git.
* Создай форк оригинального репозитория в свою организацию (назови его, как требовалось: название-репозитория-твояфамилия).
* Открой этот форк в GitHub Codespaces.
* Создай новую ветку wip и переключись на нее. В терминале Codespaces введи:
git checkout -b wip
Шаг 2. Создание проекта
Теперь нужно создать React-приложение с помощью Vite.
* В терминале запусти команду создания проекта (точка в конце означает "создать в текущей папке", если у тебя пустой репозиторий. Если нужно создать подпапку, замени . на shelf-app):
npm create vite@latest . -- --template react
* Установи зависимости:
npm install
* Запусти сервер для разработки, чтобы видеть изменения в реальном времени:
npm run dev
Шаг 3. Создание файловой структуры
Согласно условию, каждый компонент должен лежать в своей папке с .jsx и .css файлом. Создай в папке src следующую структуру:
src/
├─ components/
│ ├─ BookForm/
│ │ ├─ BookForm.jsx
│ │ └─ BookForm.css
│ ├─ Checkbox/
│ ├─ Input/
│ ├─ Button/
│ ├─ FilterChip/
│ ├─ BookList/
│ │ ├─ BookList.jsx
│ │ └─ BookList.css
│ └─ BookItem/
│ ├─ BookItem.jsx
│ └─ BookItem.css
├─ pages/
│ └─ ShelfScreen/
│ ├─ ShelfScreen.jsx
│ └─ ShelfScreen.css
├─ App.jsx
└─ App.css
(Для простых компонентов вроде Input, Button, Checkbox, FilterChip также создай папки и файлы по аналогии).
Шаг 4. Написание кода (сверху вниз)
Давай напишем логику для основных файлов. Верстку и классы тебе нужно будет перенести из reference.html, здесь я показываю именно React-логику.
1. src/App.jsx (Главное хранилище состояния)
Здесь мы храним массив книг и фильтр, а также пишем функции для их изменения.
import { useState } from 'react'
import ShelfScreen from './pages/ShelfScreen/ShelfScreen'
import './App.css'
function App() {
// Состояние для списка книг
const [books, setBooks] = useState([])
// Состояние для фильтра (показывать только непрочитанные)
const [showOnlyUnread, setShowOnlyUnread] = useState(false)
// Добавление книги (иммутабельно)
const handleAddBook = (title) => {
const newBook = {
id: Date.now(),
title,
author: 'Неизвестный автор', // Можно расширить форму позже
read: false
}
setBooks(prev => [...prev, newBook])
}
// Переключение статуса "Прочитано" (иммутабельно)
const handleToggleRead = (id) => {
setBooks(prev => prev.map(book =>
book.id === id ? { ...book, read: !book.read } : book
))
}
// Удаление книги (иммутабельно)
const handleDeleteBook = (id) => {
setBooks(prev => prev.filter(book => book.id !== id))
}
return (
<div className="app-container">
<ShelfScreen
books={books}
showOnlyUnread={showOnlyUnread}
setShowOnlyUnread={setShowOnlyUnread}
onAddBook={handleAddBook}
onToggleRead={handleToggleRead}
onDeleteBook={handleDeleteBook}
/>
</div>
)
}
export default App
2. src/pages/ShelfScreen/ShelfScreen.jsx (Фильтрация и компоновка)
Этот компонент берет данные из App, фильтрует их и передает нужным блокам.
import BookForm from '../../components/BookForm/BookForm'
import BookList from '../../components/BookList/BookList'
// Импортируй FilterChip, когда создашь его
export default function ShelfScreen({
books,
showOnlyUnread,
setShowOnlyUnread,
onAddBook,
onToggleRead,
onDeleteBook
}) {
// Логика фильтрации
const filteredBooks = showOnlyUnread
? books.filter(book => !book.read)
: books;
return (
<main>
<h1>Моя полка</h1>
<BookForm onAdd={onAddBook} />
{/* Здесь можно использовать твой компонент FilterChip */}
<label>
<input
type="checkbox"
checked={showOnlyUnread}
onChange={() => setShowOnlyUnread(prev => !prev)}
/>
Показывать только непрочитанные
</label>
<BookList
books={filteredBooks}
onToggleRead={onToggleRead}
onDeleteBook={onDeleteBook}
/>
</main>
)
}
3. src/components/BookForm/BookForm.jsx (Локальный state)
Форма хранит в себе то, что вводит пользователь, и очищается после отправки.
import { useState } from 'react'
// Импортируй свои Input и Button, если вынес их в отдельные компоненты
export default function BookForm({ onAdd }) {
const [title, setTitle] = useState('')
const handleSubmit = (e) => {
e.preventDefault() // предотвращаем перезагрузку страницы
const trimmedTitle = title.trim()
if (trimmedTitle) {
onAdd(trimmedTitle)
setTitle('') // Очищаем поле после добавления
}
}
return (
<form onSubmit={handleSubmit}>
<input
type="text"
value={title}
onChange={(e) => setTitle(e.target.value)}
placeholder="Название новой книги..."
/>
<button type="submit">Добавить на полку</button>
</form>
)
}
4. src/components/BookList/BookList.jsx (Рендеринг списка)
Просто принимает отфильтрованный массив и рендерит карточки.
import BookItem from '../BookItem/BookItem'
export default function BookList({ books, onToggleRead, onDeleteBook }) {
if (books.length === 0) {
return <p>Здесь пока пусто...</p>
}
return (
<div className="book-list">
{books.map(book => (
<BookItem
key={book.id}
book={book}
onToggle={onToggleRead}
onDelete={onDeleteBook}
/>
))}
</div>
)
}
5. src/components/BookItem/BookItem.jsx (Карточка книги)
Глупый (dumb) компонент, который просто красиво выводит переданные пропсы.
export default function BookItem({ book, onToggle, onDelete }) {
// Цветовая палитра для обложек-заглушек
const colors = ['#f87171', '#60a5fa', '#34d399', '#fbbf24', '#a78bfa']
const coverColor = colors[book.id % colors.length]
return (
<div className="book-item">
<div className="book-cover" style={{ backgroundColor: coverColor }}>
{/* Обложка */}
</div>
<div className="book-info">
<h3>{book.title}</h3>
<p>{book.author}</p>
<label>
<input
type="checkbox"
checked={book.read}
onChange={() => onToggle(book.id)}
/>
Прочитано
</label>
<button onClick={() => onDelete(book.id)}>Удалить</button>
</div>
</div>
)
}
Шаг 5. Как сдать работу
Когда перенесешь все стили из reference.html в соответствующие .css файлы и проверишь, что приложение работает:
* Зафиксируй изменения (Commit):
В терминале (или через вкладку Source Control слева) выполни:
git add .
git commit -m "feat: реализована логика приложения Shelf на React"
* Отправь ветку в репозиторий (Push):
git push origin wip
* Захости работу (Deploy):
* Если вы используете GitHub Pages, настрой его в настройках репозитория (Settings -> Pages), либо используй Vercel / Netlify (по инструкции вашего преподавателя).
* Создай Pull Request:
* Зайди на страницу своего форка на GitHub.
* Появится зеленая кнопка "Compare & pull request" для твоей ветки wip.
* Важно: убедись, что ты создаешь PR в ветку main своего же форка, а не в оригинальный репозиторий преподавателя (выбери base: main и compare: wip).
* В поле "Reviewers" (справа) добавь пользователя ktkv419.
* Оставь PR открытым — преподаватель проверит и смержит его сам.