Истории успеха наших клиентов — лучшие проекты
Вход/ Регистрация

Как создать и развернуть приложение на React: быстрый и простой деплой с App Platform

1039
11 минут чтения
Средний рейтинг статьи: 5

React — это популярная JavaScript-библиотека, предназначенная для создания пользовательских интерфейсов. Её главная особенность — компонентная архитектура, что позволяет разработчикам легко создавать и управлять сложными веб-приложениями. В этой статье мы рассмотрим, как создать React-приложение и выполнить его деплой на сервер с использованием Timeweb Cloud.

Создание приложения

Для примера мы создадим простой TO-DO-лист, который позволит пользователям добавлять задачи, организовывать их по колонкам и управлять ими.

Подготовка

Перед началом работы убедитесь, что у вас установлен Node.js. Если его нет, скачайте последнюю версию с официального сайта Node.js и установите, следуя инструкциям на экране.

Разработка

С установленным Node.js можно приступать к созданию React-приложения.

Инициализация проекта

  1. Консоль: Запустите консоль Windows или другой терминал на вашем устройстве.

  2. Выбор пути: Переместитесь в папку, где будет размещен проект. Например, чтобы перейти на рабочий стол, выполните команду:

    
cd Desktop
  1. Создание проекта: Введите команду для создания нового React-проекта:

    
npx create-react-app to-do-list

Название to-do-list можно заменить на любое другое по вашему выбору.

  1. Редактор: Откройте проект в текстовом редакторе.

Написание кода

Во время разработки убедитесь, что все команды для установки зависимостей и запуска проекта выполняются в корневой директории проекта. Если появляются ошибки, убедитесь, что находитесь в правильной папке. Все необходимые файлы будут размещены в каталоге src.

Для локального запуска приложения введите команду npm start. Эта команда откроет проект в новой вкладке браузера.

Image1

Перед началом написания кода установим лишь одну библиотеку — fortawesome, которая предназначена для того, чтобы использовать различные иконки.

    
npm install --save @fortawesome/react-fontawesome @fortawesome/free-solid-svg-icons @fortawesome/fontawesome-svg-core

Откройте файл App.js и напишите код для основного компонента приложения.

    
import React, { useState, useEffect } from 'react'; import Column from './Column'; import './App.css'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faPlus } from '@fortawesome/free-solid-svg-icons'; function App() { // Функция для загрузки данных из localStorage или начальные данные const loadColumns = () => { const savedColumns = localStorage.getItem('columns'); return savedColumns ? JSON.parse(savedColumns) : [ { id: 1, name: 'Нужно сделать', tasks: [] }, { id: 2, name: 'Делается', tasks: [] }, { id: 3, name: 'Готово', tasks: [] }, ]; }; // Состояние для хранения колонок, инициализируется данными из loadColumns const [columns, setColumns] = useState(loadColumns); // useEffect для сохранения колонок в localStorage при их изменении useEffect(() => { localStorage.setItem('columns', JSON.stringify(columns)); }, [columns]); // Функция для добавления новой колонки const addColumn = () => { const newColumnId = Date.now(); // Уникальный идентификатор для новой колонки setColumns([...columns, { id: newColumnId, name: 'Новая колонка', tasks: [] }]); }; // Функция для удаления колонки по её идентификатору const removeColumn = (id) => { setColumns(columns.filter(column => column.id !== id)); }; // Функция для добавления новой задачи в колонку const addTask = (columnId) => { setColumns(columns.map(column => column.id === columnId ? { ...column, tasks: [...column.tasks, { id: Date.now(), text: 'Без названия', isEditing: false }] } : column )); }; // Функция для обновления текста задачи const updateTask = (columnId, taskId, newText) => { setColumns(columns.map(column => column.id === columnId ? { ...column, tasks: column.tasks.map(task => task.id === taskId ? { ...task, text: newText, isEditing: false } : task ) } : column )); }; // Функция для удаления задачи из колонки const removeTask = (columnId, taskId) => { setColumns(columns.map(column => column.id === columnId ? { ...column, tasks: column.tasks.filter(task => task.id !== taskId) } : column )); }; // Функция для начала редактирования задачи const startEditingTask = (columnId, taskId) => { setColumns(columns.map(column => column.id === columnId ? { ...column, tasks: column.tasks.map(task => task.id === taskId ? { ...task, isEditing: true } : task ) } : column )); }; // Функция для перемещения задачи между колонками const moveTask = (fromColumnId, toColumnId, taskId) => { if (fromColumnId === toColumnId) return; // Нельзя перемещать задачу в ту же колонку const fromColumn = columns.find(column => column.id === fromColumnId); const toColumn = columns.find(column => column.id === toColumnId); if (!fromColumn || !toColumn) return; const taskToMove = fromColumn.tasks.find(task => task.id === taskId); setColumns(columns.map(column => { if (column.id === fromColumnId) { return { ...column, tasks: column.tasks.filter(task => task.id !== taskId) }; } if (column.id === toColumnId) { return { ...column, tasks: [...column.tasks, taskToMove] }; } return column; })); }; return ( <div className="app-container"> <div className="columns-container"> {columns.map(column => ( <Column key={column.id} column={column} removeColumn={removeColumn} addTask={addTask} removeTask={removeTask} updateTask={updateTask} startEditingTask={startEditingTask} moveTask={moveTask} /> ))} <button className="add-column-btn" onClick={addColumn}> <FontAwesomeIcon icon={faPlus} /> </button> </div> </div> ); } export default App;

Теперь создайте новый файл Column.js. В нём будет находиться код для создания колонок, в которые мы будем помещать задачи:

    
import React, { useState, useRef, useEffect } from 'react'; import Task from './Task'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faTrash, faPencil } from '@fortawesome/free-solid-svg-icons'; function Column({ column, removeColumn, addTask, removeTask, updateTask, startEditingTask, moveTask }) { const [isEditingName, setIsEditingName] = useState(false); const [columnName, setColumnName] = useState(column.name); const inputRef = useRef(null); useEffect(() => { if (isEditingName && inputRef.current) { inputRef.current.focus(); // Фокус на input при начале редактирования имени колонки } }, [isEditingName]); useEffect(() => { setColumnName(column.name); // Обновление локального состояния при изменении имени колонки }, [column.name]); // Обработка события переноса задачи в колонку const handleDrop = (e) => { e.preventDefault(); const taskId = e.dataTransfer.getData('taskId'); const fromColumnId = parseInt(e.dataTransfer.getData('fromColumnId'), 10); if (fromColumnId && taskId) { moveTask(fromColumnId, column.id, parseInt(taskId, 10)); } }; // Завершение редактирования имени колонки и обновление его const handleNameChange = () => { setIsEditingName(false); if (columnName.trim() === '') { setColumnName('Без названия'); } else { updateColumnName(column.id, columnName); } }; // Начало редактирования имени колонки const startEditingColumnName = () => { setIsEditingName(true); }; // Обновление имени колонки в родительском компоненте const updateColumnName = (columnId, newName) => { setColumnName(newName); // Можно добавить метод в App.js для обновления названия колонки }; return ( <div className="column" data-id={column.id} onDragOver={(e) => e.preventDefault()} onDrop={handleDrop}> <div className="column-header"> {isEditingName ? ( <input ref={inputRef} type="text" value={columnName} onChange={(e) => setColumnName(e.target.value)} onBlur={handleNameChange} onKeyPress={(e) => { if (e.key === 'Enter') { handleNameChange(); } }} className="column-name-input" /> ) : ( <h3 className="column-name">{columnName}</h3> )} <div className="column-actions"> <button onClick={startEditingColumnName} className="edit-column-btn"> <FontAwesomeIcon icon={faPencil} /> </button> <button onClick={() => removeColumn(column.id)} className="remove-column-btn"> <FontAwesomeIcon icon={faTrash} /> </button> </div> </div> {column.tasks.map(task => ( <Task key={task.id} task={task} removeTask={() => removeTask(column.id, task.id)} updateTask={(newText) => updateTask(column.id, task.id, newText)} startEditingTask={() => startEditingTask(column.id, task.id)} /> ))} <button className="add-task-btn" onClick={() => addTask(column.id)}>Добавить задачу</button> </div> ); } export default Column;

Также создайте файл Task.js и внутри него напишите код, чтобы создавать задачи: 

    
import React, { useState, useRef, useEffect } from 'react'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faTrash, faPencil } from '@fortawesome/free-solid-svg-icons'; function Task({ task, removeTask, updateTask }) { const [isEditing, setIsEditing] = useState(false); const [taskText, setTaskText] = useState(task.text); const inputRef = useRef(null); useEffect(() => { if (isEditing && inputRef.current) { inputRef.current.focus(); // Фокус на input при начале редактирования задачи } }, [isEditing]); // Начало редактирования задачи const handleEdit = () => { setIsEditing(true); }; // Сохранение изменений задачи const handleSave = () => { setIsEditing(false); if (taskText.trim() === '') { setTaskText('Без названия'); } updateTask(taskText); }; // Обработка события перетаскивания задачи const handleDragStart = (e) => { e.dataTransfer.setData('taskId', task.id); e.dataTransfer.setData('fromColumnId', e.target.closest('.column').dataset.id); }; return ( <div className="task" draggable onDragStart={handleDragStart} > {isEditing ? ( <input ref={inputRef} type="text" value={taskText} onChange={(e) => setTaskText(e.target.value)} onBlur={handleSave} onKeyPress={(e) => { if (e.key === 'Enter') { handleSave(); } }} className="task-input" /> ) : ( <span className="task-text">{taskText}</span> )} <div className="task-actions"> <button onClick={handleEdit} className="edit-task-btn"> <FontAwesomeIcon icon={faPencil} /> </button> <button onClick={removeTask} className="remove-task-btn"> <FontAwesomeIcon icon={faTrash} /> </button> </div> </div> ); } export default Task;

После завершения разработки вы можете снова запустить приложение командой npm start и убедиться, что оно работает корректно. Приложение позволяет добавлять, редактировать и удалять задачи и колонки, а также перемещать задачи между колонками. Данные сохраняются локально, поэтому при перезагрузке страницы ничего не пропадет.

Image6

Деплой React-приложения

Теперь, когда приложение готово, его нужно развернуть на сервере. В этом примере мы воспользуемся Timeweb Cloud.

App Platform

Готовое окружение для быстрого деплоя
и тестирования проектов из GitHub, GitLab, Bitbucket
или любого другого git-репозитория.

Для начала необходимо загрузить файлы проекта в систему контроля версий Git. Вы можете использовать любую платформу для хранения репозиториев, но в данном случае мы будем использовать проект, размещенный на GitHub.

Запуск в Timeweb Cloud App Platform

Чтобы развернуть React-приложение, воспользуемся сервисом App Platform от Timeweb Cloud.

  1. Войдите в свой аккаунт Timeweb Cloud и создайте новый проект.

Image4

  1. В разделе «App Platform» выберите тип «Frontend» и фреймворк React.

Image8

  1. Подключите свой репозиторий с проектом. 
  2. Выберите регион для минимизации задержки.

Image7

  1. В конфигурации выбираете лимит запросов, который нужен. В настройках приложения ничего не меняйте.

Image3

  1. В информации о приложении укажите его название и комментарий (если требуется).

Image5

  1. Нажмите «Запустить деплой» для автоматического разворачивания приложения на сервере. После успешного завершения процесса вы получите сообщение Deployment successfully completed.
  2. Теперь вы можете открыть приложение в браузере, используя предоставленный домен, и убедиться, что всё работает как нужно.

Image2

Заключение

В этой статье мы разобрали, как создать React-приложение и развернуть его на сервере с помощью Timeweb Cloud. Следуя этим шагам, вы сможете быстро освоить процесс деплоя и использовать платформу для размещения своих веб-приложений.

1039
11 минут чтения
Средний рейтинг статьи: 5
Хотите внести свой вклад?
Участвуйте в нашей контент-программе за
вознаграждение или запросите нужную вам инструкцию
img-server
Пока нет комментариев