Думать на React

React, по нашему мнению, используется главным образом для создания больших, быстрых веб-приложений с помощью JavaScript. Он очень хорошо масштабируется  для нас на Facebook и Instagram.

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

 

Начните с макета

Представьте, что у нас уже есть JSON API и макет от нашего дизайнера. Наш дизайнер, по-видимому, не очень хорош, потому что экспериментальная модель выглядит следующим образом:

Mockup

Наш JSON API возвращает некоторые данные, которые выглядят следующим образом:

[
  {category: "Sporting Goods", price: "$49.99", stocked: true, name: "Football"},
  {category: "Sporting Goods", price: "$9.99", stocked: true, name: "Baseball"},
  {category: "Sporting Goods", price: "$29.99", stocked: false, name: "Basketball"},
  {category: "Electronics", price: "$99.99", stocked: true, name: "iPod Touch"},
  {category: "Electronics", price: "$399.99", stocked: false, name: "iPhone 5"},
  {category: "Electronics", price: "$199.99", stocked: true, name: "Nexus 7"}
];

Шаг 1: Разбейте пользовательский интерфейс на компоненты иерархии

Первое, что нужно сделать, это нарисовать рамки вокруг каждого компонента (и подкомпонента) в макете и дать им всем имена. Если вы работаете с дизайнером,  возможно он, уже сделал это,  пойдите и поговорить с ним! Названия слоев в Photoshop могут  в конечном итоге имена ваших React компонентов!

Но как определить что должно стать компонентом? Просто использовать тот же подход, как и при принятия решения, что должно стать функцией или объектом. Одним из таких подходов — принцип единой ответственности, то есть компонент в идеале должен делать только одну вещь. Если он заканчивает расти, он должен быть разделён на более мелкие подкомпоненты.

Поскольку вы часто отображаете пользователю данные модели в формате JSON, вы обнаружите, что при правильном  построении модели, ваш пользовательский интерфейс (и, следовательно, ваш компонент структуру) будет отображаться красиво. Это потому, что пользовательский интерфейс и данные модели, как правило придерживаться той же информационной архитектуры, что часто делает работу по разделению вашего пользовательского интерфейса на компоненты простой. Просто разбить интерфейс на составные части, которые представляют собой ровно одну часть вашей модели данных.

Component diagram

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

  1. FilterableProductTable (оранжевый): содержит пример
  2. SearchBar (синий): принимает все  пользовательские вводы
  3. ProductTable (зелёный): отображает и фильтрует данные коллекции на основе пользовательских вводов
  4. ProductCategoryRow (бирюзовый): отображает заголовки каждой категории
  5. ProductRow (красный): отображает строку с каждым продуктом

Если вы посмотрите на ProductTable, вы увидите, что заголовок таблицы (содержащий метки «Name» и «Цена») не является его собственный компонент. Это вопрос предпочтений, и есть аргументы за и против. Для этого примера, мы оставили его как часть ProductTable, потому что он является частью отображения коллекции данных, которые связаны с ProductTable. Однако, если этот заголовок станет сложным (например, добавим возможность сортировки по нажатию),  безусловно, имеет смысл сделать этот собственный компонент ProductTableHeader.

Теперь, когда мы определили компоненты на нашем макете, давайте определим их в иерархию. Это просто. Компоненты, которые появляются внутри другого компонента в макете должен появиться как дочерние элементы в иерархии:

  • FilterableProductTable
    • SearchBar
    • ProductTable
      • ProductCategoryRow
      • ProductRow

Шаг 2: Сборка статической версии в React:

Теперь, когда у вас есть иерархия компонентов, пришло время реализовать приложение. Самый простой способ построить вариант, который принимает модель данных и предоставляет пользовательский интерфейс, но не имеет интерактивности. Лучше всего, чтобы отделить эти процессы, так как построение статической версии требует много печатать без обдумывания, а добавление интерактивности требует много размышлений и меньше печати. Посмотрим, почему.

Создавая статичную версии приложения, которая отображает модель данных, вы создадите компоненты, которые будут повторно использоваться другими компонентами и передавать данные с использованием свойств. Свойства это способ передачи данных от родительского элемента к дочернему. Если вы знакомы с понятием состояния, не используйте состояние вообще, чтобы построить эту статическую версию. Состояния зарезервированы только для интерактивности, то есть для данных, которые изменяются с течением времени. Так как это статическая версия приложения, состояния вам не нужны.

Вы можете строить сверху вниз или снизу вверх. То есть, вы можете начать с построения компонентов лежащих выше в иерархии (то есть начиная с FilterableProductTable) или с теми, которые ниже в ней (ProductRow). В более простых примерах, как правило, легче идти сверху вниз, а на более крупных проектах, легче снизу вверх и писать тесты.

В конце этого шага, вы будете иметь библиотеку повторно используемых компонентов, которые отображают вашу модель данных. Компоненты будут содержать только методы render(), поскольку это статическая версия приложения. Компонент в верхней части иерархии (FilterableProductTable) примет вашу модель данных в качестве свойства. Если вы вносите изменения в вашей базовой модели данных, вызывайте ещё раз ReactDOM.render() и пользовательский интерфейс будет обновлен. Легко заметить, как ваш пользовательский интерфейс обновляется при внесении изменений и при этом ничего сложного не происходит. React сохраняет однонаправленный поток данных (называемый также односторонним связыванием) .

Просто обратитесь к документации React, если вам нужна помощь при выполнении этого шага.

Краткая интерлюдия: свойство против состояния

Есть два типа «модели» данных в React: свойства и состояние. Важно понимать различие между этими двумя; изучайте официальную документацию React, если вы не уверены в том, что разница.

Шаг 3: Определение минимального (но полного) представления состояния UI

Для того, чтобы сделать ваш UI интерактивным, надо иметь возможность вызывать изменения в вашей базовой модели данных. React делает это легко с состояниями.

Для правильного построения приложения, вам в первую очередь необходимо думать о минимальном наборе изменяемых состояний, соответствующих вашим потребностям. Ключевым моментом здесь является НП (DRY): Не повторяться (Don’t Repeat Yourself). Выясните абсолютное минимальное представление необходимых состояний и вычислите все остальное, что нужно по требованим. Например, если вы создаете список, или просто держать массив элементов TODO спика (списка дел, планов); не сохраняйте в отдельную переменную состояния подсчет голосов. Вместо этого,  просто возьмите длину элементов массива TODO.

 

Подумайте о всех фрагментах данных в приложении из нашего примера. У нас есть:

  • Список продуктов
  • Поле ввода текста для поиска
  • Элемент checkbox
  • Список фильтруемых продуктов

Давайте рассмотрим каждый из них и выяснить, какие у них состояния. Просто задайте  три вопроса по каждой части данных:

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

  1. Оно передаётся от родителей через свойства? Если да, то это, вероятно, не является состоянием.
  2. Оно остаётся неизменным в течением времени? Если да, то это, вероятно, не является состоянием.
  3. Можно ли его вычислить его на основе любого другого состояния или свойства в компоненте? Если да, то это, вероятно, не является состоянием.

Оригинальный список продуктов передается в качестве свойства, так что это не состояние. Поле ввода и флажок, кажутся  состоянием, так как они изменяются с течением времени и не могут быть вычислена от чего-либо. И, наконец, отфильтрованный список продуктов не является состоянием, так как она может быть вычислен путем объединения исходного списка продуктов с возможностью поиска по полю ввода и значению флажка.

Таким образом, в конце концов, наше состояние:

  • Текст для поиска, который ввел пользователь
  • Значение флажка

Шаг 4: Определите где должно жить ваше состояние

Таким образом, мы определили минимальный набор состояний приложения. Далее, нам необходимо определить, какой компонент меняется или содержит состояние.

Помните: Все в React односторонний поток данных вниз по компонентной иерархии. Оно не может быть сразу понятно, какой компонент должен владеть в каком состоянии. Часто для новичков это очень сложно понять, поэтому следующие шаги должны помочь разобраться:

Для каждого фрагмента состояния вашего приложения:

  • Определите каждый компонент, который отображает что-то основанное на этом состоянии.
  • Найти общего предка (родительский компонент)  (это один из компонентов, которые находятся выше других в иерархии).
  • Либо общий владелец, либо другой компонент выше в иерархии должен принадлежать состоянию.
  • Если вы не можете найти компонент, который будет содержать состояние, создайте новый компонент просто для хранения состояния и добавьте его куда-нибудь выше по иерархии относительно общего компонента

Давайте рассмотрим эту стратегию для нашего приложения:

  • ProductTable нуждается в фильтрации списка продуктов, основываясь на состоянии, а SearchBar должен отобразить текст для поиска и проверить состояние.
  • Общий компонент —  FilterableProductTable.
  • Имеет смысл для проверять текст фильтрации, чтобы жить в FilterableProductTable

Круто, поэтому мы решили, что наше состояние живет в FilterableProductTable. Во-первых, добавим экземпляр свойства this.state = {filterText: '', inStockOnly: false} в конструктор FilterableProductTable, чтобы отобразить начальное состояние вашего приложения. Затем передайте filterText и inStockOnly к ProductTable и SearchBar в качестве свойства. И, наконец, используем эти свойства для фильтрации строк в ProductTable и для установки значения полей формы в SearchBar.

 

Вы увидите, как ваше приложение будет вести себя так: установите filterText на «ball» и обновите ваше приложение. Вы увидите, что таблица данных обновляется правильно.

Step 5: Добавление обратного потока данных

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

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

 

Если вы пытаетесь ввести или установите флажок в текущей версии примера, вы увидите, что React игнорирует ввод. Это сделано специально, так как мы установили свойство  value принадлежащего input, всегда быть равным state переданному FilterableProductTable.

Давайте подумаем о том, что мы хотим получить. Мы хотим убедиться, что всякий раз, когда пользователь изменяет форму, мы обновляем состояние, чтобы отобразить ввод данных пользователем. Поскольку компоненты должны обновить только свое собственное состояние, FilterableProductTable будет принимать обратный вызов (callback) от  SearchBar , и это будет срабатывать всякий раз при обновлении состояния. При вводе данных мы можем использовать событие onChange чтобы получить уведомление о поступающих данных. И обратный вызов будет принят FilterableProductTable и вызовет setState(), приложение будет обновлено.

Хотя это кажется сложным, на самом деле это всего несколько строк кода. И это действительно покажет, как распространяются ваши данные по всему приложению.

Вот и все

Будем надеяться, что это дало вам представление о том, как думать (размышлять) о при создании компонентов и приложений с React. Хотя мы написали немного больше чем привыкли при построении прототипа, помните, что читать код приходится гораздо чаще, чем писать, а модульность упрощает чтение. Когда вы начнёте строить большие библиотеки компонентов, вы по достоинству оцените эту ясность и модульность, и с повторным использованием кода, ваши строки кода начнут сокращаться. 🙂