В предыдущей части мы реализовали простой сервер отдыха, который возвращает компании и сотрудников. На этот раз мы добавим некоторые функции внешнего интерфейса (с небольшими изменениями на сервере), чтобы увидеть, как Reason может помочь нам (или нет :)) в этой области. Reason поставляется с проектом ReasonReact, который, конечно же, использует React под капотом. Но кое-что другое.

Процесс сборки

Начнем с изменения процесса сборки. В прошлый раз я использовал инструмент bsb bucklescript для компиляции файлов re, а затем webpack, чтобы склеить созданные файлы js вместе. Это работало хорошо, пока я не попытался включить файл css в файл re. Css-файл находился в каталоге src, в то время как webpack искал его в выходном каталоге. Решением для этого было использование плагина bs-loader для webpack. С тех пор все заработало как положено. Базовый файл webpack действительно прост:

Смотреть и чувствовать

Посмотрим на финальную версию приложения. Frontend действительно прост и состоит из трех компонентов: Header, CompanyList и SearchResults.

CSS происходит от Kube framework.

Заголовок

В TypeScript это определение компонента могло бы выглядеть так:

Определение компонента без сохранения состояния в Reason немного отличается:

Во-первых, в каждом модуле может быть только один компонент. Поскольку каждый файл в Reason является модулем, вам не нужно объявлять имя модуля внутри файла. Если вы хотите, чтобы в одном файле было больше компонентов, просто используйте вложенные модули, например:

а затем используйте это так:

<Header.SimpleHeader/>

ReasonReact не использует классы для компонентов. Вместо этого он использует записи с переопределяемыми полями. Это означает, что нет указателя this - записи неизменяемы. Записи в Reason определяются аналогично объектам JS (но они не являются объектами - они фиксируются в именах и типах полей и транслируются в массивы JavaScript, что делает их действительно эффективными). Объявим подпись записей:

..затем запись экземпляра:

Напротив, названия типов модулей начинаются со строчной буквы. Теперь для изменения записи используется оператор распространения.

Выглядит знакомо? Выглядит просто? Хороший :)

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

Давайте еще раз взглянем на определение заголовка. Первая часть…

… Создает запись компонента с реализациями методов жизненного цикла по умолчанию (например, didMount, render, shouldUpdate и т. Д.)

Вторая часть…

… Определяет функцию make, которая вызывается при каждом вызове JSX и возвращает другую запись с переопределениями пользователя. Обратите внимание на оператор распространения … компонент! ReasonReact.statelessComponent не может быть встроен здесь, чтобы избежать воссоздания компонента при каждом повторном рендеринге (компонент потеряет свое фактическое состояние).

Свойства компонента объявлены как параметры функции make. Если вы не знаете - в Reason двойное двоеточие :: является именованным параметром. Подчеркивание перед именем параметра указывает компилятору не предупреждать, если этот параметр не используется.

Компонент заголовка - это компонент без сохранения состояния, и он передает только обратные вызовы от основного приложения своему элементу ввода поиска.

Одно неудобство в ReasonReact (по крайней мере, на данный момент) - это явное преобразование строк в элементы React. Поэтому вместо того, чтобы писать:

нужно использовать функцию ReasonReact.stringToElement или, как в моем случае, псевдоним для нее с именем str:

CompanyList, SearchResults

CompanyList также является компонентом без сохранения состояния. Что здесь интересно, так это явные определения типов именованных параметров:

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

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

Приложение

Намного более интересным является компонент App. Это единственный компонент с отслеживанием состояния в приложении.

В этом примере я использовал компонент с отслеживанием состояния со встроенным редуктором для обработки всех изменений состояния. Также существует версия statefulComponent для «более классической» обработки состояния.

Версия Reducer основана на способе обработки изменений состояния Redux. Теоретически это не более чем:

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

В ReactReason нет необходимости в redux, поскольку этот способ обработки состояния компонента был встроен в него.

Но в первую очередь нам нужно начальное состояние:

Затем необходимо определить тип действия со списком всех действий компонента:

Действия могут принимать полезную нагрузку. Например, когда будет выполнено действие CompaniesLoaded, оно появится со списком компаний.

Для обработки действий должна быть предусмотрена функция редуктора:

В большинстве случаев его реализация сводится к сопоставлению с образцом по аргументу действия. Каждое действие должно возвращать новое состояние в варианте update. Можно выбрать несколько, но в большинстве случаев будут использоваться ReasonReact.Update, ReasonReact.NoUpdate и ReasonReact.UpdateWithSideEffects. Последний позволяет обновить состояние и выполнить функцию после этого, но до следующего рендеринга. В ReactJs это было бы эквивалентом:

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

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

в updateQuery:

и для executeQuery:

executeQuery получает данные с сервера и передает их редуктору. CompanyService - это клиент для отдыха, который использует bs-fetch ​​(привязки для выборки) и bs-json (библиотека для кодирования / декодирования json в Bucklescript).

И это почти все :)

Резюме

Reason и ReasonReact все еще очень свежи, но кажутся очень удобными даже сейчас. Здесь и там присутствует ненужная многословность (например, let query = (ReactDOMRe.domElementToObj (ReactEventRe.Form.target event)) ## value или необходимость в ключевом слове fun в каждый контекст) или странные синтаксические ограничения (например, пробелы вокруг дочерних элементов JSX), но общее впечатление (после привыкания к новому языку и его концепциям) очень положительное. Этот мощный язык довольно прост в изучении и имеет знакомый синтаксис. Это большой плюс. ReasonReact немного отличается от оригинального React, но для тех, у кого есть опыт работы с ReactJs / Redux, это не должно быть большой проблемой.

Ссылки

1. Источник примера приложения

2. ReasonML

3. ReasonReact

4. Приложение Todo в Reason

5. Приложение First Reason React для разработчиков Javascript