Elm Architecture для React (Часть 2)

Эксперимент в архитектуре приложения React

В моем последнем посте я показал, как с помощью React переводить самые простые примеры Elm Architecture на JavaScript. В этом посте я продолжу это упражнение и объясню, как с помощью этого шаблона справиться с побочными эффектами.

Первым примером Elm Architecture, который вводит побочные эффекты, является Пример RandomGif. Чтобы реализовать это, мы должны изменить контракт наших функций update и init. Они больше не возвращают просто модель, а набор моделей и эффектов.

В Elm подпись типа функции update выглядит так:

update : Action -> Model -> (Model, Effects Action)

У нас нет эквивалента Effects в JavaScript, но мы можем приблизить его, определив эффект как функцию, которая создает обещание, которое преобразуется в действие.

Зритель RandomGif

Итак, давайте просто переведем пример RandomGif на JavaScript. Начнем с функции init:

Это довольно легко перевести:

Одно отличие состоит в том, что мы используем флаг error, чтобы определить, не удалось ли выполнить запрос. Если gifUrl имеет значение null, а error - false, запрос выполняется, но если error истинно, то запрос по какой-то причине завершился неудачно. Мы делаем это, потому что устанавливаем для gifUrl значение null, когда пользователь запрашивает другое изображение, чтобы мы могли немедленно сообщить, что запрос выполняется. Это не самый элегантный подход, но для этого примера он подойдет.

Далее следуют действия и функция update:

А вот перевод на JavaScript:

Реализовать функцию getRandomGif тоже довольно просто. Он должен возвращать эффект, то есть функцию, которая возвращает обещание, которое преобразуется в действие:

Здесь мы используем API fetch ​​. Вам понадобится полифилл для браузеров, которые изначально его не поддерживают.

Теперь нам просто не хватает функции view:

Теперь нам нужно изменить наш компонент контейнера React, чтобы иметь дело с эффектами. В дополнение к модели, update и view потребуется дополнительное свойство effects. Это будут начальные эффекты.

Как видите, мы запускаем начальные эффекты в ловушке жизненного цикла componentDidMount. Любые эффекты, возвращаемые update, запускаются после вызова setState.

А вот реализация runEffects:

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

Список RandomGif

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

Наша модель будет иметь свойство темы и список моделей RandomGif. Итак, наша функция инициализации выглядит так:

Есть три возможных действия. Изменение темы, добавление нового RandomGif и действия для перенаправления действия на конкретный RandomGif.

Это может показаться немного устрашающим. Давайте рассмотрим это шаг за шагом.

Topic принимает событие и возвращает действие. Это действие просто обновляет свойство темы нашей модели в соответствии с тем, что ввел пользователь.

Добавить инициализирует новую модель RandomGif с текущим значением свойства topic. Функция RandomGif.init возвращает кортеж модели и эффектов. Эти эффекты будут создавать обещания, которые приводят к действию RandomGif, поэтому нам нужно будет каким-то образом перенаправить эти действия в наше действие Gif, которое вызовет RandomGif.update с соответствующей моделью. . Мы делаем это с помощью функции mapEffects. Посмотрим, как это работает позже.

Gif просто перенаправляет действие RandomGif на конкретную модель RandomGif в нашем gifList, поэтому оно параметризуется с помощью индекса. Мы также должны передать эффекты, которые возвращает RandomGif.update.

Функция mapEffects похожа на функцию forward в том, что она ожидает фабричную функцию действий, которая будет применяться к любому разрешению эффектов. Его реализация довольно проста:

И наконец, что не менее важно, функция view:

Вывод

На этом эксперимент завершен. Полный код с большим количеством примеров, включая отмену / повтор и реализацию виртуального дома, находится на Github. Запросы на извлечение приветствуются, но имейте в виду, что это всего лишь эксперимент и не предназначен для реального использования.

Как всегда, не стесняйтесь обращаться ко мне в Twitter, если у вас есть какие-либо вопросы или комментарии.