Видеоуроки, интерактивный редактор и сохранение прогресса — бесплатно, сразу после входа.
ВойтиСоздать аккаунт — бесплатноЗакончили урок?
Войдите, чтобы отмечать прогресс
В этом решении мы разбиваем систему комментариев на независимые переиспользуемые компоненты с чёткими границами ответственности.
CommentsHeader отображает заголовок с количеством комментариев. Мы выделили шапку отдельно, потому что это независимый визуальный блок, который может располагаться в любом месте интерфейса. Компонент принимает число через prop count и отображает его в скобках. В будущем здесь может появиться сортировка комментариев или фильтрация.
CommentAuthor объединяет имя пользователя и время публикации. Мы вынесли информацию об авторе в компонент, потому что это логически связанные данные с общей стилизацией. Имя выделяется жирным через <strong>, время показывается рядом. Этот компонент можно переиспользовать в разных типах комментариев или постов.
LikeButton - это переключатель состояния лайка. Мы выделили кнопку лайка отдельно, потому что она имеет собственную визуальную логику - меняет иконку и класс в зависимости от состояния liked. Тернарный оператор выбирает заполненное сердце ❤️ для лайкнутых и пустое 🤍 для остальных. Счётчик likes всегда отображается рядом с иконкой.
CommentActions группирует все доступные действия над комментарием. Мы создали этот компонент, чтобы изолировать панель действий от остального содержимого комментария. Компонент использует LikeButton для лайка и обычную кнопку для ответа. В будущем сюда легко добавить редактирование, удаление или жалобы.
CommentItem собирает вместе автора, текст и действия. Мы выделили комментарий в компонент, чтобы инкапсулировать структуру отдельного комментария. Компонент получает весь объект comment и извлекает из него нужные поля для дочерних компонентов. Стрелочные функции () => onLike(comment.id) создают замыкания с id комментария, чтобы передать его при вызове.
Мы оборачиваем вызовы функций в стрелочные функции, чтобы передать comment.id как аргумент. Функции onLike и onReply принимают id, но CommentActions не должна знать про id - она просто вызывает переданные callback-функции. Замыкание сохраняет comment.id и передаёт его при клике на кнопку.
CommentsList - это контейнер для множества комментариев. Мы выделили список отдельно, чтобы отделить логику отображения множества элементов от логики одного элемента. Компонент проходит по массиву comments и создаёт CommentItem для каждого. Функции onLike и onReply пробрасываются дальше без изменений.
AddComment инкапсулирует форму добавления комментария. Мы вынесли форму в компонент, потому что она имеет собственную логику - валидацию и горячие клавиши. Компонент использует textarea вместо input, чтобы поддерживать многострочные комментарии. Атрибут disabled={!value.trim()} блокирует кнопку, пока поле пустое или содержит только пробелы.
Условие !value.trim() проверяет, что после удаления пробелов строка пустая. Метод trim() убирает пробелы с начала и конца строки. Если результат пустой, ! превращает его в true, и кнопка блокируется. Это предотвращает отправку бессмысленных комментариев из одних пробелов.
Функция создаёт новый комментарий и добавляет его в конец массива. Проверка newComment.trim() не позволяет добавить пустой комментарий, дублируя валидацию кнопки для случая отправки через Ctrl+Enter. Мы создаём объект с полями id, user, text, time, likes, liked - той же структурой, что у существующих комментариев. Значение time: 'только что' показывает, что комментарий свежий.
Функция изменяет состояние лайка для комментария с нужным id. Для найденного комментария мы создаём новый объект с изменёнными полями likes и liked. Тернарный оператор comment.liked ? comment.likes - 1 : comment.likes + 1 уменьшает счётчик, если лайк уже был (снятие лайка), и увеличивает, если лайка не было. Оператор !comment.liked инвертирует булево значение - переключает состояние.
Функция находит комментарий по id и подставляет упоминание автора в поле ввода. Метод find возвращает первый элемент, для которого условие истинно, или undefined. Шаблонная строка `@${comment.user} ` создаёт текст типа "@Алексей " с пробелом в конце, чтобы пользователь мог сразу начать печатать ответ.
Обработчик проверяет комбинацию Ctrl+Enter. Условие e.key === 'Enter' проверяет, что нажата клавиша Enter, e.ctrlKey проверяет, что зажата клавиша Ctrl. Если обе проверки истинны, вызывается функция добавления комментария. Это стандартная комбинация для быстрой отправки в мессенджерах и соцсетях.
Компонент App собирает три части системы комментариев вместе. Структура линейная - шапка, список, форма добавления. Всё состояние (comments, newComment) и бизнес-логика (добавление, лайки, ответы, горячие клавиши) остаются в App. Компоненты получают данные и callback-функции через props. CommentsHeader автономна, CommentsList управляет отображением списка и действиями над элементами, AddComment управляет вводом нового комментария.