TL; DR (иначе, просто скажите мне, в чем вы ошибались)

  1. Передавать appState всем компонентам - не лучшая идея. appState содержит неразрешенные возможные состояния, о которых отдельные компоненты обычно не заботятся, но которые вам все равно придется обрабатывать, поскольку ReasonML - это строго типизированный язык.
  2. Вместо передачи состояния передайте ctx (контекст). Мой текущий подход заключается в передаче ctx записи каждому компоненту, который содержит разрешенную информацию, производную из состояния, которая требуется конкретному компоненту . Это позволяет компоненту функционировать с определенными знаниями о контексте, в котором он будет отображаться.
  3. Передача appSend всем компонентам (как и раньше) - это нормально - это просто функция, с помощью которой мы влияем на состояние приложения.

И более подробно…

Я писал о методе поддержки состояния приложения в ReasonReact еще в апреле, и с тех пор я работал с ReasonML намного больше, завершив преобразование кодовой базы Тураку с ES6 в Reason, и оказалось, что мой подход, описанный в предыдущей статье, не работал так хорошо, как я ожидал.

Передавать состояние приложения как appState - плохая идея с ReasonML. Передавать appSend вроде нормально.

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

Я продолжу предполагать, что вы смотрели видео.

Один из инструментов, который ReasonML дает нам для описания вещи, которая может быть одним из набора вещей, называется Variant. В определении состояния приложения Тураку есть типы, которые выглядят примерно так (упрощенно):

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

Варианты позволяют компилятору ReasonML гарантировать, что всякий раз, когда мы обрабатываем значение типа вариант, учитывались все его возможности. Например, в корневом компоненте, когда мы пытаемся определить, какое представление отображать, мы могли бы использовать switch, например:

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

Dashboard содержит основной пользовательский интерфейс, с которым должен взаимодействовать зарегистрированный пользователь. Отправим appState и appSend

… А затем загляните внутрь Dashboard, чтобы увидеть, как мы можем отобразить список доступных команд:

Теперь проблема должна быть очевидна. appState является вариантом (или может содержать вложенные варианты), и его содержимое можно получить только через сопоставление с образцом. Из-за этого компонент вынужден обрабатывать все возможные случаи, хотя мы знаем, что панель мониторинга никогда не будет отображаться без входа пользователя в систему.

К счастью, исправить это просто. Это необходимо для того, чтобы не передавать состояние приложения.

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

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

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

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

Да, но только частично. Обратите внимание, что мы все еще передаем appSend, что позволяет нам исключить функции обратного вызова. appSend предоставляет компонентам доступ ко всем действиям редуктора состояния приложения.

Здесь также появляется запись ctx. Я обнаружил, что очень удобно обернуть все в одну запись, чтобы я мог передавать ее функциям - это также помогает избежать худших последствий сверления опор - там, где вам придется нанизать новые свойства на функции внутри компонентов, чтобы доставить их туда, где они должны быть. Оборачивая реквизиты в ctx и рассматривая его как набор значений, к которым можно обращаться везде, где это необходимо, мы ограничиваем нарезание резьбы / сверление точками входа и выхода компонентов.

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

Очевидно, что использование записи ctx необязательно. Передача отдельных опор также работает, и выбор между обертыванием опор или оставлением их неупакованными, по-видимому, является решением между уменьшением накладных расходов на бурение опор или улучшением чистоты места вызова компонентов.

Так какой вывод?

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

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

Кредиты

Искусство Рекхи Соман: www.rekhasoman.com

Первоначально опубликовано на blog.harigopal.in 8 сентября 2018 г.