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

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


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


Макет



Наше 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 в виде пользовательского интерфейса, тем быстрее вы придете к тому, что если модель данных построена корректно, то и ваш пользовательский интерфейс (и, следовательно, структура компонентов) выглядит красиво. Причина в том, что пользовательский интерфейс и модель данных, как правило, придерживаются одной и той же информационной архитектуры, а это означает, что разделение пользовательского интерфейса на составляющие часто является тривиальной задачей — "Просто разбейте его на компоненты, которые представляют ровно один кусочек вашей модели данных".


Диаграмма компонентов



Как видно из макета — у нас есть пять компонентов в нашем простом приложении. Мы выделили разноцветными боксами каждый из компонентов.


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


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


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


  • FilterableProductTable
  • SearchBar
  • ProductTable
  • ProductCategoryRow
  • ProductRow


Шаг 2: Создаем статическую версию в React





HTML


CSS


JavaScript


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


Реализация статической версии — это отображение (рендеринг) модели данных, в процессе которого вы создаете Компоненты, которые используют другие Компоненты и передают данные посредством props (Свойства). props являются инструментом передачи данных от предка к потомку в иерархии Компонентов React. В React есть такое понятие, как state (Состояние) — никогда не используйте Состояние (state) при создании статической версии. Основное предназначение Состояния — интерактивность, оно необходимо для передачи и фиксирования данных, которые меняются с течением времени. Поскольку, в данный момент, вы создаете статическую версию приложения — вы не нуждаетесь в использовании Состояния.


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


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


Небольшое отступление: Свойства (props) и Состояние (state)


В React существуют две "модели" данных: props и state. Важно понимать различие между ними. Если вы не уверены, что знаете разницу — перечитайте соответствующий раздел официальной документации.


Шаг 3: Определяем минимальное (но достаточное) представление Cостояния (state) пользовательского интерфейса


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


Для построения корректного приложения, в первую очередь, вам необходимо обдумать минимальный набор изменяемых состояний необходимых для вашего приложения. Придерживайтесь принципа DRY: Don't Repeat Yourself (Не повторяйся). В идеале — минимальное представление Состояния вашего приложения не должно содержать чего либо, что может быть вычислено на основании имеющихся данных (в props и state) в момент времени, когда это необходимо. Например: Если вы строите приложение, отображающее список дел, оставайтесь в пределах массива, содержащего записи дел — не создавайте отдельной переменной Состояния, отображающей количество дел. Вместо этого, в тот момент когда вам необходимо отобразить количество дел — просто возьмите длину имеющегося массива.


Продумаем все единицы данных в нашем приложении. Мы имеем:


  • Оригинальный список продуктов
  • Поисковый текст, введенный пользователем
  • Значение чекбокса
  • Отфильтрованный список продуктов


Давайте пройдемся по каждому пункту и определим — является ли он Состоянием. Для каждой единицы данных нам надо задать три вопроса:


  1. Передаются ли эти данные посредством props от предка? Если да, то это скорее всего не Состояние.
  2. Остаются ли эти данные неизменными с течением времени? Если да, то это скорее всего не Состояние.
  3. Можете ли вы вычислить эти данные на основании имеющихся (в props и state) в вашем Компоненте? Если да, то это не Состояние.


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


В итоге, наши Состояния:


  • Поисковый текст, введенный пользователем
  • Значение чекбокса


Шаг 4: Определяем где наше Состояние будет размещаться





HTML


CSS


JavaScript


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


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


Для каждой единицы Состояния вашего приложения:


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


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


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


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


Вы можете запустить приложение и посмотреть как оно будет вести себя: установите значение filterText в конструкторе Компонента FilterableProductTable равным 'ball' и перезагрузите приложение. Вы увидите, что таблица данных корректно обновлена.


Шаг 5: Добавляем обратный поток данных





HTML


CSS


JavaScript


К текущему шагу мы создали приложение, которое корректно передает и использует props и state двигаясь по иерархии Компонентов вниз. Настало время добавит поддержку передачи данных в обратном направлении: компонентам формы, которые расположены ниже по иерархии, необходимо каким-то образом обновлять Состояние в Компоненте FilterableProductTable.


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


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


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


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


На этом все


Надеюсь, этот туториал дал вам представление о том, как следует мыслить при построении компонентов и приложений React. Хотя нам пришлось набрать немного больше кода, чем вы привыкли, помните, что код читается в разы чаще, чем пишется, а наш код читается легко за счет прозрачности и модульности. Как только вы начнете создавать большие библиотеки или приложения — вы по настоящему оцените эту прозрачность и модульность, а возможность повторного использования неизменно приведет к тому, что вы будете набирать все меньше и меньше кода.

источник: https://m.habrahabr.ru

Report Page