Валерий Шестернин
React JS - это популярный фреймворк для разработки пользовательских интерфейсов. В этой статье мы рассмотрим историю развития React JS, его основные концепции, преимущества и возможности, а также примеры кода для более полного понимания работы с этим фреймворком.
История создания и развития
React JS был создан в 2013 году командой Facebook. Первоначально он был разработан для упрощения построения интерфейсов в веб-приложениях и решения проблемы обработки большого количества информации в Facebook и Instagram.
Фреймворк был впервые представлен на конференции JSConf US в мае 2013 года. Уже в июле того же года Facebook выпустил React под открытой лицензией. React привлек внимание разработчиков своим упором на повышение производительности за счет использования виртуального DOM и упрощением процесса разработки за счет использования компоненов.
В 2014 году был выпущен React 0.12, который добавил поддержку ES6. В тот же год Facebook представил архитектуры Flux и потом Redux, о которых часто говорят в связи с React, хотя они не являются его обязательной частью.
В 2015 году Facebook выпустил React Native, позволяющий использовать React для разработки мобильных приложений.
В 2016 году была представлена версия React 15, в которой появилась поддержка SVG и улучшенная работа с ошибками.
В 2017 году вышла версия React 16, также известная как React Fiber, которая включает в себя полностью переписанный алгоритм рендеринга и добавляет поддержку асинхронного рендеринга.
И, наконец, в 2018 году в React 16.8 были добавлены Hooks, которые позволили использовать состояния компонентов и другие функци без написания класса.
С тех пор выходило несколько минорных обновлений, каждое из которых привносило новые улучшения и доработки. В настоящий момент React JS является самым популярным фреймворком для разработки прогрессивных веб-приложений (PWA). Технология PWA в отличии от статичной верстки на HTML, CSS и ванильном JavaScript визуально и функционально трансформирует сайт в приложение, способное реагировать на действия пользователя не перезагружая страницу для изменения элементов страницы или отрисовки новых элементов.
React часто (даже в официальной документации) называют библиотекой, а не фреймворком потому что он не имеет встроенного архитектурного шаблона. React JS позволяет разработчику выбрать методологию организации проекта в зависимости от его сложности, потребности в масштабируемости и скорости написания кода. Такая свобода действий может принести и вред в виде запутанной структуры проекта и дублирования кода поэтому следует относиться к ней с осторожностью и сразу правильно организовывать работу. Далее в статье мы рассмотрим основные концепции React JS и примеры работы с ними.
Виртуальный DOM
DOM (Document Object Model) - это промежуточное представление веб-страницы в памяти браузера. Однако, взаимодействие с реальной DOM может быть дорогостоящим в плане производительности того что все ее элементы связаны и изменение одного из них может привести перерисовке других элементов. React JS решает эту проблему с помощью виртуальной DOM - легковесной копия реальной DOM, которая не отрисовывается браузером. React JS использует ее для отслеживания изменений в состоянии приложения. Не стоит путать вируальную DOM с браузерной технологией под названием теневой DOM (Shadow DOM), предназначенной в основном для определения области видимости переменных и CSS в веб-компонентах. Этот подход позволяет производить множество изменений в приложении с гораздо более высокой производительностью, чем при напрямую работе с реальным DOM. В итоге, это приводит к более плавному и эффективному обновлению пользовательского интерфейса.
Синтаксис JSX
Для описания внешнего вида пользовательского интерфейса React JS не использует HTML как таковой. Вместо него UI описывается с помощью JSX (JavaScript XML), который является расширением синтаксиса JavaScript. Оно позволяет писать HTML-подобный код непосредственно внутри JavaScript файлов в React JS. JSX делает код более читабельным и понятным, позволяя разработчикам объединить логику и представление в одном файле.
Сейчас в React JS принято использовать функциональный подход и каждый JSX файл содержит React-компоненты, которые представляют собой функцию, которая возвращает HTML-подобную разметку.
//Создадим простой JSX файл
import "./TodoList.css"; //подключение стилей
export function TodoList() {
//наша функция
return (
//спецтальный React-тег Fragment
<>
{/* заголовок первого уровня */}
<h1>Список дел</h1>
{/* поле ввода */}
<input type="text" />
{/* кнопка */}
<button>Добавить</button>
</>)}
```Если Вы уже знакомы с HTML, на примере выше вы увидите знакомые теги, кроме родительского, который представляет собой “пустое” объявление тега. Это специальный React-тег под названием Fragment. Функция, описаная в JSX файле может возвращать только один родительский элемент с любым количеством вложенных и Fragment предназначен для обхода этого ограничения, если вам не нужна обертка в виде div или другого элемента. В остальном описание разметки очень похоже на HTML, “магия” начинается, когда мы добавляем в нее интерактивность. В отличии от обычного взаимодействия с помощью JavaScript, JSX позволяет получить доступ к элементам страницы, слушателям событий и прочему без использования селекторов. Так же сам React предоставляет нам мощный инструмент - хуки (hooks). Это функции, с помощью которых мы можем управлять состоянием и взаимодействовать с жизненным циклом React-компонента. Одним из основных и наиболее часто используемых хуков - `useState`, который необязательным параметром принимает изначальное состояние в виде любого значения и возвращает переменную, хранящую это состояние и функцию для его изменения. Для того что бы продемонстрировать использование данного хука и доступ к элементам разметки, добавим их в наш компонент TodoList:
```ts
import "./TodoList.css";
import { useState } from "react";//импортируем хук из библиотеки React
export function TodoList() {
const [todo, setTodo] = useState();
//создаем состояние и функцию для ее изменения с помощью хука useState
return (
<>
<h1>Список дел</h1>
<input type="text" onChange={(e) => setTodo(e.target.value)} />
{/* при вводе в поле input состояние будет обновляться */}
<button onClick={() => console.log(todo)}>Добавить</button>
{/* при нажатии на кнопку введенная строка отобразится в консоли браузера */}
</>)}
Как видно из примера выше, благодаря тому что разметка описана в одном файле с JavaScript, мы можем удобно управлять элементами без лишнего кода, а использование React-хуков еще сильнее упрощают написание логики.
Компонентный подход
React строится на основе компонентной архитектуры. Компоненты — это независимые и переиспользуемые блоки, которые объединяют структуру, логику и стили в одном месте. Благодаря композиции он позволяет создавать компоненты, которые могут содержать другие компоненты, и строить сложные пользовательские интерфейсы из более простых компонентов. Например наше приложение со списком дел нуждается в компоненте для отображения элементов этого списка.
import "./TodoItem.css"; //каждый компонент может подключать свои стили
export const TodoItem = () => {
const name = "Купить молока";
return (
<li>
<p>{name}</p>
<button>Удалить</button>
</li>)};
Теперь мы можем добавить TodoItem в TodoList или любой другой компонент.
import { TodoItem } from "./TodoItem";//импортируем компонент
import "./TodoList.css";
import { useState } from "react";
export function TodoList() {
const [todo, setTodo] = useState();
return (
<>
<h1>Список дел</h1>
<input type="text" onChange={(e) => setTodo(e.target.value)} />
<button onClick={() => console.log(todo)}>Добавить</button>
<TodoItem />
{/* Синстаксис добавления компонента напоминает добавление тега */}
</>
)}
```Компонентный подход в React JS является одной из основных концепций этого фреймворка. Он позволяет модульно организовать код приложения, что упрощает понимание логики и структуры проекта. Компоненты могут быть повторно использованы в различных частях приложения, что сокращает дублирование кода и улучшает его поддержку.
# Односторонний поток данных
Наш компонент элемента списка пока ни как не взаимодействует с остальным приложением и просто отображает имеющиеся в нем данные. Что бы это изменить нужно наладить передачу данных между компонентами. В React реализован однонаправленный поток данных от родительского компонента к дочерним. Данные передаются через пропсы (props) - значения, которые передаются дочернему компоненту от его родителя. Родительский компонент может также передавать функции (обработчики событий) в дочерние компоненты через пропсы. Когда данные изменяются в родительском компоненте, React обновляет все зависимые дочерние компоненты, используя новые значения пропсов. Однако сам дочерний компонент не имеет возможности влиять на данные, передаваемые ему через пропсы. Вместо этого, если дочернему компоненту требуется изменить данные, он вызывает функцию-обработчик, переданную ему через пропсы, и передает новые значения в родительский компонент. Что бы это продемонстрировать создадим в родительском компоненте функцию для изменения удаления элемента списка и передадим ее в дочерний компонент.
```ts
export const TodoList = () => {
const [todos, setTodos] = useState([]);
const [inputValue, setInputValue] = useState("");
//теперь мы не передаем содержимое input прямо в список дел, а храним
//отдельно что бы при нажатии на кнопку "добавить" пополнить список
const addTodo = () => {
if (inputValue !== "") {
setTodos([...todos, inputValue]);
setInputValue("")}};
//для добавления элемента в список дел создадим новую функцию
const deleteTodo = (index) => {
const newTodos = [...todos];
newTodos.splice(index, 1);
setTodos(newTodos)};
//и функцию для удаления элемента из списка дел
return (
<div>
<h1>Todo List</h1>
<input
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
/>
<button onClick={addTodo}>Add Todo</button>
<ul>
{todos.map((todo, index) => (
//теперь мы можем прямо в разметке взять массив с нашим списком дел
//и с помощью метода map передать каждый элемент массива в новый
//компонент TodoItem
<TodoItem
key={index}
//React подразумевает что каждому компоненту определяется уникальный ключ
//для корректной работы с DOM. В данном примере в качестве ключа мы используем
//индекс элемента в массиве.
name={todo}
//Родительский компонент передает в дочерние информацию об элементах списка дел
index={index}
//Индекс каждого элемента в массиве
deleteTodo={deleteTodo}
//И функцию для изменения состояния в котором хранится массив
/>
))}
</ul>
</div>)};
```Теперь дочерний компонент может использовать переданные данные, которые получил через Props.
```ts
export const TodoItem = ({ name, deleteTodo, index }) => {
//получаем переданные значения
return (
<li>
<p>{name}</p>
{/* используем их для отрисовки компонента */}
<button onClick={() => deleteTodo(index)}>Удалить</button>
{/* и для вызова переданных функций с нужными параметрами */}
</li>)};
Таким образом, изменение состояния происходит только в родительском компоненте, и новое состояние передается односторонним потоком данных от родительского компонента к дочерним компонентам. Дочерние компоненты могут изменять только те состояния родительского, доступ к которым им был предоставлен. Это позволяет React эффективно обновлять компоненты, синхронизировать интерфейс с актуальными данными и помогает упростить понимание и отслеживание изменений в приложении. Он также помогает сделать код более предсказуемым и легче поддерживаемым.
Заключение
React.js - мощный фреймворк для создания эффективных и модульных интерфейсов. Он предлагает виртуальный DOM, компонентный подход и экосистему инструментов. Изучение React.js позволяет разработчикам создавать интерактивные и высокопроизводительные веб-приложения благодаря его гибкости и надежности. 
Комментарии
0