Мы собираемся создать диаграмму, используя библиотеку визуализации данных JavaScript React под названием React Vis, переводя исходный код JavaScript из Advanced Visualization with React Vis в ClojureScript. Попутно мы узнаем о взаимодействии Fulcro и ClojureScript. Было бы хорошо, если бы вы могли прочитать исходную статью, а если вы новичок в Fulcro, вот несколько рекомендуемых материалов для чтения: Руководство по началу работы с Fulcro.

Исходный код находится на GitHub. Предполагая, что у вас уже установлен npm, запустите эту команду из каталога проекта, чтобы установить и запустить приложение:

make shadow

Вам будет предложено выполнить различные другие команды. Просто CTRL-C и снова make shadow и продолжайте, пока не будет удовлетворен shadow-cljs. Затем укажите в браузере http: // localhost: 3449.

В исходной статье есть три компонента React. В переведенной статье они тщательно сохранены и названы NaiveChart для первой наивной попытки и StaticChart и InteractiveComponents для второй попытки, которая затем ускоряется за счет упрощения линий диаграммы. В Fulcro компоненты определяются с помощью макроса defsc. Обратите внимание, что есть несколько дополнений: Game, PlayerYear и DesiredLabels, которые не имеют функции рендеринга, но существуют для поддержки компонентов React Vis, обеспечивая все их потребности в данных через входящие реквизиты.

Чтобы понять отношения между компонентами / объектами Fulcro, вы можете использовать инструмент, который позволяет вам просматривать состояние клиента запущенного приложения. Этот инструмент уже включен в проект, вам просто нужно установить указанное здесь расширение Chrome: Fulcro Inspect.

Чтобы получить больше «перспективы кода», поищите в исходном коде mprim / get-one. Это даст вам высокий уровень "времени разработки" всего приложения. Данные для отношений "к одному" обычно предоставляются статически - вы можете увидеть это, выполнив поиск по запросу prim / get-initial-state и заметив точное соответствие: все отношения "к одному" заполнены как часть загрузки начального состояния во все компоненты defsc. В этом приложении «статический» означает перед загрузкой игровых данных. По мере загрузки игровых данных отношения mprim / get-many заполняются игровыми данными игрока.

Я должен сказать, что mprim - это мое собственное пространство имен, а get-one и get-many предназначены «только для целей документации». Я считаю полезным четко указать мощность соединения поля (в терминологии базы данных соединение поля будет столбцом внешнего ключа). Альтернативой может быть расшифровка количества элементов полевого соединения по его имени. Fulcro унаследовал от Om Next, где рисование реквизита из другого компонента просто prim / get-query. Итак, вот действительно крошечное пространство имен mprim:

(def get-one prim/get-query)

(def get-many prim/get-query)

Вы можете использовать эти поисковые запросы по принципу «получить один / получить много» и Fulcro Inspect, чтобы нарисовать себе диаграмму сущностей и взаимосвязей приложения. Глядя на него, вы узнаете денормализованную структуру пользовательского интерфейса приложения. Он денормализован, потому что такие компоненты, как PlayerYear, появляются много раз: нормализованная диаграмма не будет иметь таких повторов. Работа Fulcro - превратить нормализованную базу данных (состояние приложения) в денормализованное дерево свойств для пользовательского интерфейса компонентов React.

Конечно, вносимые вами изменения, известные как мутации, переводятся в нормализованное состояние. У нас есть только одна мутация в этом приложении, которая называется fill-желаемые-метки:

Эта мутация запускается (используйте prim / transact! для запуска мутаций) сразу после начальной загрузки игроком лет со связанными игровыми данными. В клиентской базе данных : player-year / by-id есть таблица, которая индексируется. Таким образом, выполнение на нем vals дает нам все записи таблицы. Эти записи игрового года были установлены во время начальной загрузки, когда : ui / desire? был помечен с использованием def из пространства имен констант. Мы собираем идентификаторы и используем их для создания вектора идентификаторов. Затем этот вектор идентификаторов помещается в таблицу : required-labels / by-id под объединением полей : items. Желаемые метки - это игровые годы, которые отображаются на графике без необходимости движения мыши. Как вы уже догадались, : singleton используется в качестве соглашения, чтобы указать, что в таблице : desire-labels / by-id есть только одна запись.

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

(prim/merge-component! reconciler comp d)

Здесь согласователь - это точка входа в Fulcro, comp будет фактическим компонентом defsc PlayerYears, а d - данными. Формат данных всегда будет картой, где любые значения поля «ко многим» являются векторами карт. Здесь есть только одно поле «ко многим»: : games. (: games в исходном коде называлось gameData).

Помимо мутации, единственным другим местом, где происходит реальная обработка (т.е. где код не является декларативным), является создание : items на карте d:

Здесь вы можете увидеть, например, что если для этапа параметра конфигурации задано значение : Superior (другие этапы - : naive и : nyt), сезон игр игрока (то есть его линия на графике) будет упрощен.

Вот вам и Фулькро. А как насчет «взаимодействия»? Вам будет предложено npm установить необходимые библиотеки JavaScript. В этом случае импорт немного сложен. JavaScript:

import {scaleLinear} from ‘d3-scale’;
import simplify from ‘simplify-js’;

Эквивалент ClojureScript:

[“d3-scale” :refer (scaleLinear)]
[“simplify-js” :default simplify]

Используйте эту таблицу для переводов: Использование пакетов NPM.

Еще одна важная вещь, которую необходимо сделать, - это преобразовать структуры данных между двумя мирами с помощью clj- ›js, js-› clj и #js . Множество примеров в коде.

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