React: точка входа. Базовые примеры (1)

5 (100%) 3 vote[s]

Любой современный веб-проект не обходится без использовании JavaScript-фреймворков, и одними из самых популярных являются Vue и React. Традиционно в Word Press запрашиваемый пользователем файл отправляется после длинной серии взаимодействия между базой данных, внутренним кодом, сервером, браузером и уровнями кэширования; а контент обновляется при помощи интерфейса CMS (Word Press или другой, например, Drupal). Разработчик знает, где «живет» весь сайт. Сегодня уже очевидна необходимость изучения нового подхода к созданию и работе сайтов, основанном на REST API WordPress с безголовой CMS или JAMstack, например, с генератором статичных сайтов Gatsby (или Hugo) на платформе Netlify. Скорость загрузки и обновления интерфейса сегодня становится приоритетным. Как показывает статистика кривой обучения, первые шаги и понимание принципов работы даются быстрее при изучении Vue, но React труднее даётся к пониманию на первых шагах, тогда как дальнейшее изучение проходит более быстрыми темпами.



Но это всё потом. Что вначале? Взять начинающего программиста, знакомого с базовыми понятиями JavaScript, HTML & CSS? Все когда-то начинали с позиции котенка, тыкающегося носом. Ведь в сети уйма руководств по изучению React и Vue, и в них утонуть — дело 2-х минут. Как всегда, нужно брать работающие примеры и разбирать, что у них под капотом.

Вот, пожалуй, самые показательные и простенькие примерчики, по которым можно сразу сложить впечатление об архитектуре и принципах работы React. Практика по этой статье — не больше 30 мин.

В двух словах о React

Начинать изучение с голой теории — не самое лучшее вхождение в тему. Особенно, если у вас уже сложились какие-то стереотипы. А при изучении React они будут ломаться оптом. Можно кое-что почитать, принять к сведению, и заняться практикой. Поначалу все равно в голове осядет процентов 10. Потому что многое придется переосмыслить. Как выяснилось, понимание теории происходит намного быстрее, если потратить некоторое время на простую игру с фреймворками, покрутив их и поработав прямо в консоли.

Вот, например, что говорит теория:

React является декларативным. Это означает именно то, что означает. Другими словами: React позволяет разработчикам декларативно описывать пользовательские интерфейсы и управлять действиями над их состоянием, вместо того, чтобы выполнять действия над каждым элементом DOM. Вместо того чтобы придумывать шаги для описания действий над интерфейсами, разработчики просто описывают интерфейсы в терминах «конечного» состояния (например, функции). Когда действия происходят с этим состоянием, React на основе этого позаботится об обновлении пользовательских интерфейсов в DOM.

API-интерфейс браузера, который известен как DOM API, работает по такому принципу: при каждом событии (набор текста, прокрутка, изменение размера экрана) браузер заново перерисовывает всю страницу. Поэтому всегда разработчику нужно было избегать частого обхода дерева DOM, так как любая операция в DOM выполняется в одном и том же потоке. А это, в свою очередь, сказывалось на замедлении работы приложения.

React сначала генерирует виртуальное представление DOM и сохраняет его в памяти для дальнейшего использования. Затем он продолжит выполнять операции DOM, отображая его в браузере. Когда мы сообщаем React обновить дерево ранее визуализированных элементов, оно генерирует новое виртуальное представление обновленного дерева. Теперь React имеет 2 версии дерева в памяти. Чтобы отобразить обновленное дерево в браузере, React сравнивает две виртуальные версии DOM, имеющиеся в памяти, вычисляет различия между ними, выясняет, какие элементы в основном дереве необходимо обновить, и обновляет в браузере только их. Этот принцип и называется алгоритмом согласования.

Звучит неплохо, но для новичка слишком абстрактно.

А теперь на практике. В сети есть хороший пример, который мы также рекомендуем для тестирования для понимания React.

Таймер с React

Для начала разберемся с подключением компонентов React. Есть 2 способа начать работу с React: просто подключить в Head необходимые компоненты:

  1. <script crossorigin="" src="https://unpkg.com/react@16.13.0/umd/react.production.min.js"></script>
  2. <script crossorigin="" src="https://unpkg.com/react-dom@16.13.0/umd/react-dom.production.min.js"></script>
  3. <script crossorigin="" src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
  4. <!--Или Babel отсюда:
  5. <script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.26.0/babel.min.js" integrity="sha256-FiZMk1zgTeujzf/+vomWZGZ9r00+xnGvOgXoj0Jo1jA=" crossorigin="anonymous"></script>
  6.  -->

или создать локальную среду разработки:

npx create-react-app my-app

Мы пока ничего не будем загружать, а просто покрутим React в консоли, используя первый способ.

Небольшое отступление. В React используется ES2015 (ES6), поэтому следует уделить внимание его изучению. Традиционного ванильного JS уже недостаточно. Например, вы будете использовать JSX, который в чистом виде браузер не понимает. Чтобы он работал, нужен транспайлер — программа, которая выполнит транспиляцию JSX в JS. В нашем случае таким транспайлером будет выступать Babel, который мы подключили в хедере файла. Можете на сайте посмотреть онлайн конвертер, как Babel превращает JSX в JS.

Чтобы иметь все последние версии подключаемых файлов, рекомендуем использовать форму поиска на сайте https://cdnjs.com/.

Кроме того, в React вы столкнётесь:

  1. Со стрелочными функциями, например:
// обычная запись
var sum = function() {
  return [].reduce.call(arguments, function(m, n) {
    return m + n;
  }, 0);
}
 
// эквивалент стрелочной записи
var sum = (...args) =&gt; args.reduce((m, n) =&gt; m + n, 0);

2. С интерполяцией строк — это замена заполнителей в строке значениями строковой переменной. В JS интерполяция поддерживается только при использовании обратных кавычек (клавиша  `ё`  на клавиатуре):

var age = 25;
// С обычными кавычками интерполяция не поддерживается
console.log('I am ${age} years old'); // Вывод: I am ${age} years old
console.log("I am ${age} years old"); //Вывод: I am ${age} years old
// Поддерживается только с обратными
console.log(`I am ${age} years old`); //Вывод: I am 25 years old

3. С блочными зонами видимости  – constlet вместо var.

4. С деструктуризацией объектов и массивов для извлечения данных из массивов и объектов:

//Раньше:
var f = function() {
  return ['this', 'is', 'array'];
};
 
var tmp = f(),
    first = tmp[0], 
    second = tmp[1], 
    third = tmp[2];
 
console.log(first, second, third); // this is array
 
//В ES6:
 
var f = function() {
  return ['this', 'is', 'array'];
};
 
// ES6 destructuring для массивов
var [ first, second, third ] = f();
 
console.log(first, second, third); // this is array

6. С новыми методы массивов: Array.from, find, fill, includes, Array.of .

5. При работе в локальной среде вы столкнетесь с модулями.

Все остальное вы «подтянете» по мере изучения React, сталкиваясь с новым синтаксисом или новыми конструкциями.

Теперь пример. Создадим два одинаковых таймера. Один — используя API Web DOM напрямую, второй — используя React API, и сравним их работу в консоли:

<!doctype html>
<html>
<head>
<script crossorigin src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>
<script crossorigin src="https://cdnjs.cloudflare.com/ajax/libs/typescript/3.8.3/typescript.min.js"></script>
<style>
#mountNode, #mountNode2, #mountNode3, pre {
margin-left:auto;
margin-right:auto;
margin-top:10px;
padding:20px;
border-radius:4px;
box-shadow:0 0 3px rgba(0,0,0,0.4);
}
#mountNode, #mountNode2 {width:300px;}
</style>
</head>
<body>
<div id="mountNode"></div>
<div id="mountNode2"></div>
<script>
//API Web DOM
const render = () => {
  document.getElementById('mountNode').innerHTML = `
    <div>
      Hello HTML
      <input />
      <pre>${new Date().toLocaleTimeString()}</pre>
    </div>
  `;
//React API
  ReactDOM.render(
    React.createElement(
      'div',
      null,
      'Hello React',
      React.createElement('input', null),
      React.createElement('pre', null, new Date().toLocaleTimeString())
    ),
    document.getElementById('mountNode2')
  );
};

setInterval(render, 1000);
</script>

</body>
</html>

Вот результат:

Теперь самое интересное. Внешне результат работы обоих скриптов вроде бы одинаковый. Но попробуйте ввести что-нибудь в верхнее поле input, созданное при помощи API Web DOM. Все, что вы введете, будет жить ровно 1 сек. Из-за того, что перерисовывается весь DOM.

А теперь попробуйте ввести что-либо в нижний input. React позволит это сделать, и все ваши данные останутся в DOM, поскольку обновится только измененный объект, но не весь DOM.

В панели Chrome DevTools мы можем наглядно посмотреть разные способы визуального обновления DOM:

react render в Chrome DevTools

Обратите внимание! В этом примере элемент React мы создали без использования JSX, поэтому браузер не выдал ошибки. Если мы запишем элемент при помощи JSX, например:

<div id="mountNode"></div>
<div id="mountNode2"></div>
<div id="mountNode27"></div>

<script>
//HTML
const render = () => {
  document.getElementById('mountNode').innerHTML = `
    <div>
      Hello HTML
      <input />
      <pre>${new Date().toLocaleTimeString()}</pre>
    </div>
  `;

  ReactDOM.render(
    React.createElement(
      'div',
      null,
      'Hello React',
      React.createElement('input', null),
      React.createElement('pre', null, new Date().toLocaleTimeString())
    ),		
    document.getElementById('mountNode2')
  );
 //JSX  
const Time = () => ( 
  <pre> {new Date().toLocaleTimeString()} 
  </ pre> 
); 
ReactDOM.render (<Time/>,  mountNode27);
};

setInterval(render, 1000);
</script>

то в консоли получим ошибку:

react jsx щшибка babel

Чтобы подключить Babel, нужно в строке 5 явно указать

<script type="text/babel">

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

В чем смысл JSX? Ведь один и тот же компонент можно записать двумя способами, которые будут работать одинаково:

//Кнопка на JSX
function Button ( props ) {  
  return <button type = "submit"> {props.label} </ button>; 
} 
ReactDOM.render (<Button label = "Save" />, mountNode);

//Та же кнопка без JSX
function Button ( props ) { 
  return React.createElement ( 
    "button", 
    {type: "submit"}, 
    props.label 
  ); 
} 

ReactDOM.render ( React.createElement (Button, {label: "Save"}) , 
  mountNode 
);

JSX — это компромисс: вместо того чтобы писать компоненты React с использованием синтаксиса React.createElement, мы используем синтаксис, очень похожий на HTML, а затем используем компилятор для преобразования его в вызовы React.createElement. Компонент React — это функция JavaScript, которая возвращает элемент React (обычно с JSX). Когда используется JSX, <tag></tag> синтаксис становится вызовом React.createElement("tag"). Мы не пишем HTML. Мы используем расширение JavaScript для возврата вызовов функций, которые создают элементы React (которые по сути являются объектами JavaScript).

Vue, например, работает с HTML, и предоставляет язык шаблонов для него. В этом простота понимания при первичном знакомстве с Vue. Вы пишете свои динамические представления с «расширенным» синтаксисом HTML, который имеет циклы и условные выражения. Затем JavaScript преобразует шаблоны в операции DOM. Операции DOM могут затем использоваться в браузере для отображения дерева DOM, описанного расширенным HTML, приблизительно так:

<div id="root">
  <ul>
    <li v-for=’name in listOfNames’>
      {{name}}
    </li>
  </ul>
</div>
<script>
new Vue({
  el:"#root",
  data: {
    listOfNames: [‘Kevin’, ‘John’, ‘Sarah’, ‘Alice’]
  }
});  
</script>

В этих примерах мы создавали компоненты при помощи функций. В Rеаct можно создавать компоненты с использованием классов:

//Создание компонентов с использованием классов (кнопка)
class Buttons extends React.Component { 
  render () { 
    return ( 
      <button > {this.props.label} </ button> 
    ); 
  } 
} 
ReactDOM.render (<Button label = "Send" />, mountNode);

В выпуске React Hooks (начиная с React версии 16.8) был представлен новый API для придания компоненту состояния состояния (и предоставления ему многих других функций).

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

Аргументы в пользу нового API:

      • Вам не нужно работать с «экземплярами» класса и их неявным состоянием. Вы работаете с простыми функциями, которые обновляются при каждом рендере. Состояние явно объявлено и ничего не скрыто. Все это в основном означает, что в вашем коде будет меньше сюрпризов.

      • Вы можете сгруппировать связанную логику с сохранением состояния и разделить ее на отдельные составные и разделяемые блоки. Это позволяет разбить сложные компоненты на более мелкие части. Это также облегчает тестирование компонентов.

      • Вы можете использовать любую логику с сохранением состояния декларативным способом и без необходимости использовать какое-либо иерархическое «вложение» в деревья компонентов.

Вы можете встретить понятия «компонент» и «элемент» в руководствах по React:

  • React Component — это шаблон, глобальное определение. Это может быть либо функция, либо класс (с методом рендеринга).
  • Элемент React — это то, что возвращается из компонентов. Это объект, который фактически описывает узлы DOM, которые представляет компонент. С компонентом функции этот элемент является объектом, который возвращает функция, а с компонентом класса элемент является объектом, который возвращает метод рендеринга компонента. Элементы React — это не то, что вы видите в браузере. Это просто объекты в памяти, и вы ничего не можете изменить в них.
  • React внутренне создает, обновляет и уничтожает объекты, чтобы выяснить дерево элементов DOM, которое необходимо отобразить в браузере. При работе с компонентами класса принято ссылаться на их отображаемые браузером элементы DOM как на экземпляры компонентов. Вы можете визуализировать много экземпляров одного и того же компонента. Экземпляр — это ключевое слово «this», которое вы используете внутри компонентов на основе классов. Вам не нужно создавать экземпляр из класса вручную. Вам просто нужно помнить, что это есть где-то в памяти React. 

Хук в компоненте React

Хук в компоненте React — это вызов специальной функции. Некоторые из них можно использовать для обеспечения компонента функции элементами с сохранением состояния (например useState), другие можно использовать для управления побочными эффектами (например useEffect) или для кэширования/запоминания функций и объектов (например useCallback). 

Функции React Hook могут использоваться только в компонентах функций. Вы не можете использовать их в компонентах класса.

Чтение и обновление состояния

Чтобы увидеть пример базовой setStateловушки, давайте заставим компонент Button реагировать на событие click. Метод setState() добавляет в очередь на рендеринг изменения в состоянии компонента, которые должны быть отрендерены с новым состоянием. Сочетание this.state сразу после вызова setState() становится потенциальной ловушкой. 

Для этого сохраним количество нажатий на нее в переменной «count» и отобразим счетчик в блоке <P> и в метке кнопки значение этой переменной:

<!doctype html>
<html>
<head>
<script crossorigin src="https://unpkg.com/react@16.13.0/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16.13.0/umd/react-dom.production.min.js"></script>
<script crossorigin src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<!-- <script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.26.0/babel.min.js" integrity="sha256-FiZMk1zgTeujzf/+vomWZGZ9r00+xnGvOgXoj0Jo1jA=" crossorigin="anonymous"></script>-->

<style>
#mountNode9 {
width:300px;    
margin-left:auto;
margin-right:auto;
margin-top:10px;
padding:20px;
border-radius:4px;
box-shadow:0 0 3px rgba(0,0,0,0.4);
}
</style>
</head>
<body>
<div id="mountNode9"></div>
<script type="text/babel">
class Example1 extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0
    };
  }

  render() {
    return (
      <div>
        <p>Вы кликнули {this.state.count} раз(а)</p>
        <button onClick={() => this.setState({ count: this.state.count + 1 })}>
          Кнопка {<br/>}Меня тыцнули {this.state.count} раз(а)
        </button>
      </div>
    );
  }
}
ReactDOM.render(<Example1 />, mountNode9);
</script>
</body>
</html>

В отличие от версии onClickатрибута DOM (которая использует строку), onClickатрибут React использует ссылку на функцию . Вы указываете это внутри фигурных скобок.

Чтобы отслеживать обновления состояния и запускать виртуальную проверку DOM и реальную сверку DOM, React необходимо знать о любых изменениях, которые происходят с любыми элементами состояния, которые используются внутри компонентов. Чтобы сделать это эффективным способом, React требует использования специальных методов получения и установки для каждого элемента состояния, который вы вводите в компонент. Здесь как раз и используется хук useState

Вот что нам нужно для элемента состояния счетчика, который мы пытаемся реализовать:

const [count, setCount] = React.useState (0);
<!doctype html>
<html>
<head>
<script crossorigin src="https://unpkg.com/react@16.13.0/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16.13.0/umd/react-dom.production.min.js"></script>
<script crossorigin src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<!-- <script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.26.0/babel.min.js" integrity="sha256-FiZMk1zgTeujzf/+vomWZGZ9r00+xnGvOgXoj0Jo1jA=" crossorigin="anonymous"></script>-->

<style>
#mountNode9 {
margin-left:auto;
margin-right:auto;
margin-top:10px;
padding:20px;
border-radius:4px;
box-shadow:0 0 3px rgba(0,0,0,0.4);
width:300px;
}
.btn {
 background-color: #84c4a6;
 color:white;
 width:200px;
 height:50px;
}
.btn:hover {
  background-color: #1d3654;
}
</style>
</head>
<body>
<div id="mountNode9"></div>
<script type="text/babel">
const Button = () => { 
  const [count, setCount] = React.useState (0); 

  return ( 
    <button className="btn" onClick = {() => setCount (count + 1)} > 
      {count} 
    </ button> 
  ); 
}; 

ReactDOM.render (<Button />, mountNode9);
</script>
</body>
</html>

Вот, как это работает:

На что ещё стоит обратить внимание в этой статье — это на предупреждение, которое выскакивает в консоли браузера:

You are using the in-browser Babel transformer. Be sure to precompile your scripts for production ...

Объясним: поскольку мы не используем сервер Node.js, а подключили компоненты React прямо на странице, компиляция babel JSX в JS происходит в браузере, на стороне клиента. Это может быть причиной «тормоза» приложения, о чем и говорит это предупреждение. Как бы в этом ничего криминального нет. В нем рекомендуется использовать серверный рендеринг на Node.js. Если отвлечься от темы — это одна из пока что проблем — найти хостинг с поддержкой npm для развертывания react. Именно поэтому часто практикуется вариант, когда (уже существующий) сайт на WordPress живет на одном хостинге, а Node.js — на другом, и связь происходит через Rest API. (См. JAMStack).

Продолжение следует

Читайте больше по теме:

Подписаться
Уведомление о
guest
0 комментариев
Inline Feedbacks
View all comments
Просмотры: 485

Популярные записи