Node JS и React - как создать фулстек приложение. Полное руководство

2 года назад·3 мин. на чтение

React приложение и бэкенд на NodeJS это хорошее сочетание, которое подойдет для реализации практически любых сервисов. Эта статья поможет вам быстро создать фулстек приложение.

Необходимые инструменты

Перед началом нужно убедиться, что на компьютере установлены все необходимые библиотеки, IDE и ПО, а именно:
  • NodeJS и npm. Их можно скачать с официального сайта nodejs.org. npm установится автоматически вместе с NodeJS.
  • Предпочитаемый IDE, например, Visual Studio Code.
  • Опционально, установить git для удобной работы с кодом.

О приложении

В этой статье напишем приложение, которое будет получать и отображать список дел. Структура папок будет выглядеть следующим образом.
app/
  frontend/
  backend/

Создание бэкэнда на NodeJS

Запустим команду в папке app/backend для инициализации проекта:
npm init -y
Эта команда создаст файл package.json. Этот файл содержит как общую информацию о проекте (название, версия, описание и т.д.), так и информацию о зависимостях, скрипты для запуска, сборки и тестирования. Для создания сервера будем использовать express. Установим его с помощью команды:
npm i express
Создадим файл index.js, который будет содержать код для запуска сервера. Этот код запускает веб сервер на порту 3010, если он не задан в переменных среды.
// backend/index.js
const express = require('express');
 
const PORT = process.env.PORT || 3010;
const app = express();
 
app.use((req, res, next) => {
  res.setHeader('Access-Control-Allow-Origin', '*');
  next();
});
 
app.listen(PORT, () => {
  console.log(`Server listening on ${PORT}`);
});
Добавим команду для запуска сервера в package.json. В результате сможем запускать наш сервер с помощью команды npm start.
// backend/package.json
...
"scripts": {
  "start": "node ./index.js"
},
...
Из директории app/backend запустим команду npm start. Если ошибок нет, получим сообщение, что сервер прослушивает порт 3010.
PS C:\tutorials-coding\nodejs-react-app\backend> npm start        

> backend@1.0.0 start
> node ./index.js

Server listening on 3010

Создание API

API это интерфейс, с помощью которого React приложение будет общаться с веб-сервером, т.е. запрашивать, изменять или удалять данные. В нашем случае мы создадим API для получения списка дел в формате JSON. Создадим файл todo-items.json c объектами todo. Этот массив будем отдавать по запросу /api/todo-items.
[
  {
    "id": 1,
    "text": "Изучить NodeJS",
    "done": true
  },
  {
    "id": 2,
    "text": "Изучить ReactJS",
    "done": true
  },
  {
    "id": 3,
    "text": "Написать приложение",
    "done": false
  }
]
Следующий код создает эндпоинт /api/todo-items. React приложение будет отправлять GET запрос на этот эндпоинт.
// backend/index.js

// ...

const todoItems = require('./todo-items.json');
app.get('/api/todo-items', (req, res) => {
  res.json({ data: todoItems });
});

app.listen(PORT, () => {
  console.log(`Server listening on ${PORT}`);
});
Для того чтобы изменения вступили в силу, нужно перезапустить NodeJS сервер. Для остановки скрипта - в терминале, в котором запущен npm start, нужно нажать Ctrl + C (Command + C). Далее снова запускаем npm start. Для проверки эндпоинта, в браузере перейдем по адресу http://localhost:3010/api/todo-items. В результате получим, такой ответ. Ответ от Node JS сервера

Создание фронтенда на React

В папке app/ откроем новый терминал и запустим команду для создания React приложения, где frontend имя нашего приложения.
npx create-react-app@latest frontend
Дождемся установки всех зависимостей. В терминале перейдем в папку frontend.
cd ./frontend
Установим библиотеку bootstrap для дальнейшего использования готовых компонентов.
npm install react-bootstrap bootstrap
Заимпортируем bootstrap.min.css в файле frontend/src/index.js.
import 'bootstrap/dist/css/bootstrap.min.css';
Запустим приложение командой npm start.
npm start
Получим следующее сообщение. Перейдем по указанному адресу в браузере.
Compiled successfully!
 
You can now view frontend in the browser.        
 
  Local:            http://localhost:3003        
  On Your Network:  http://192.168.99.1:3003     
 
Note that the development build is not optimized.
To create a production build, use npm run build. 

Отправка HTTP запроса из React в NodeJS

К этому моменту у нас уже есть рабочий сервер, который умеет принимать запросы и отдавать данные. Сделаем запрос на /api/todo-items из React приложения. Для этого вызовем функцию fetch из хука useEffect в файле App.js.
// frontend/src/App.js
import { useState, useEffect } from 'react';
import Form from 'react-bootstrap/Form';
import './App.css';
 
function App() {
  const [todoItems, setTodoItems] = useState([]);
 
  useEffect(() => {
    fetch('http://localhost:3010/api/todo-items')
      .then((res) => res.json())
      .then((result) => setTodoItems(result.data));
  }, []);
 
  return (
    <div>
      {todoItems.map((item) => (
        <Form.Group key={item.id} className="app__todo-item">
          <Form.Check type="checkbox" checked={item.done} />
          <Form.Control type="text" value={item.text} />
        </Form.Group>
      ))}
    </div>
  );
}
 
export default App;
Открыв приложение в браузере, получим такой результат. React приложение со списком дел Исходный код

Проп key для пересоздания компонента в ReactJS

2 года назад·2 мин. на чтение

Знали ли вы, что проп key может быть полезен не только при рендеринге списка компонентов. Проп key можно использовать и для того чтобы сбросить состояние одного компонента.

Что такое проп key в ReactJS?

Это специальный проп, который может быть добавлен к любому компоненту. Он помогает механизму reconciliation (согласование), упрощая сравнение компонентов. Типичный сценарий использования key - добавление его в компоненты списка. Он нужен для того чтобы React понимал, какой компонент списка был добавлен, удален или изменен.
const notes = [
  {
    id: 1,
    title: 'React hooks',
  },
  {
    id: 2,
    title: 'JSX',
  },
  {
    id: 3,
    title: 'Redux',
  },
];

const NotesList = ({ notes, onClick }) => {
  return (
    <div className="notes-list">
      {notes.map((note) => (
        <p
          className="notes-list__item"
          key={note.id}
          onClick={() => onClick(note)}
        >
          {note.title}
        </p>
      ))}
    </div>
  );
};

Проп key работает и вне списков

Проп key может быть добавлен к абсолютно любому компоненту для того, чтобы сбросить нежелательное состояние этого компонента. Например, в списке заметок есть поле для ввода текста. Если просто добавить это поле и ввести в него текст, то при выборе новой заметки слева - текст будет сохраняться. И, предположим, при выборе заметки мы хотим очистить это поле. список заметок в приложении ReactJS
function App() {
  const [activeNote, setActiveNote] = useState();

  const handleClick = (note) => {
    setActiveNote(note);
  };

  return (
    <div className="notes-container">
      <NotesList notes={notes} onClick={handleClick} />
      <Note title={activeNote?.title} />
    </div>
  );
}
const Note = ({ title }) => {
  const [text, setText] = useState();

  const handleChange = (event) => {
    setText(event.target.value);
  };

  return (
    <div className="note">
      <p>{title}</p>
      <textarea
        className="note-textarea"
        value={text}
        onChange={handleChange}
      />
    </div>
  );
};
Это можно сделать, например, добавив проп text в компонент Note. И далее очищать его при изменении состояния activeNote. Но изменение компонентов может быть невозможным, если мы используем компоненты из third-party библиотеки.

Сброс состояние экземпляра компонента

Проп key помогает React идентифицировать компонент. Его также можно использовать, чтобы сообщить React, что идентификатор компонента изменился и это вызовет полное повторное создание этого компонента. Добавим key={activeNote?.id} к компоненту <Note />.
// ...
  return (
    <div className="notes-container">
      <NotesList notes={notes} onClick={handleClick} />
      <Note title={activeNote?.title} key={activeNote?.id} />
    </div>
  );
}
Теперь, при изменении key React пересоздаст компонент <Note />.

Влияние на производительность

Хотя это хороший прием, который уменьшает количество кода, важно иметь ввиду, что этот подход заставляет React пересоздавать весь экземпляр компонента. В примере выше большая часть компонента <Note /> будет перерисована в любом случае при изменении activeNote. Поэтому в этом случае это достаточно хорошее решение. В реальных приложениях нужно ограничивать добавление key к одиночным компонентам вне списков, а также избегать добавления key на компоненты верхнего уровня. Это может стать причиной проблем с производительностью, которые трудно обнаружить.