Clojurescript, Reagent: передавать атомы в качестве входных данных или использовать в качестве глобальных переменных?

Я пишу приложение Clojurescript, используя Reagent, чтобы сделать мои компоненты реактивными.

У меня простой вопрос. Нужно ли мне

  1. Передайте мои атомы в качестве входных данных через мои компоненты или
  2. Использовать атомы как глобальные переменные и позволить им «побочно влиять» на мои компоненты?

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

Правильно ли я говорю, что использование их в качестве глобальных переменных (помимо того, что они менее подробны при определении входных данных компонента) предотвращает повторную визуализацию целых родительских компонентов, где состояние атома не используется?


person Boyentenbi    schedule 11.07.2016    source источник


Ответы (2)


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

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

;; setup a single instance of global state
(defonce app-state
  (reagent/atom {:foo 0 :bar 0})

;; define a generic counter component that knows
;; nothing about the global state
(defn counter
  [count]
  [:div
    [:button {:onclick #(swap! count inc) "+"]
    [:span @count]])

 ;; define counter components and give them access to
 ;; specific context within the global state
 (defn app
   [state]
   [counter (reagent/cursor app-state [:foo])]
   [counter (reagent/cursor app-state [:bar])])

Вы даже можете пойти еще дальше, если решите использовать Reagent с Re-frame. Re-frame побуждает вас создавать приложение с определенной архитектурой, которая выглядит примерно так.

 app-db  >  subscriptions
   ^             
handlers        v
   ^             
 events  <  components
  1. Вместо того, чтобы просто писать компоненты и подключать их прямо к глобальному атому (app-db), вы пишете subscriptions, которые являются просто функциями, которые выбирают/запрашивают некоторые данные из app-db и передают их компонентам при каждом изменении app-db.

  2. Затем, вместо того, чтобы компонент возился с app-db напрямую, компонент создает events, которые представляют собой просто небольшие фрагменты данных, описывающие намерение компонента.

  3. Эти события отправляются в handlers, функции, которые принимают event и текущий app-db в качестве аргументов и возвращают новый app-db. Затем существующий app-db заменяется, заставляя подписчиков передавать данные компонентам и так далее.

Это определенно полезно, если вы обнаружите, что ваш проект Reagent немного запутан, а Re-frame readme отличное чтение, независимо от того, решите ли вы использовать его или нет.

person Dan Prince    schedule 11.07.2016
comment
Спасибо за Ваш ответ. Интересно, не могли бы вы уточнить, как проходящие атомы облегчают тестирование компонентов. - person Eric Auld; 08.03.2021

Я предпочитаю передавать ратом компоненту. Re-frame становится популярным https://github.com/Day8/re-frame

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

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

(defn my-component []
  (let [x (reaction (:x @db)]
    (fn []
      [:div @x]))

Этот компонент будет перерисовываться только при изменении :x (а не при изменении чего-либо в db. Создание реакций может стать утомительным, что является одной из привлекательных сторон re-frame.

(ns whip.view.reactions
  (:require [reagent.core :as reagent]
            [devcards.core :refer-macros [defcard-rg deftest]])
  (:require-macros [reagent.ratom :refer [reaction]]))
(def a (reagent/atom {:x 100 :y 200})) (def b (reaction (:x @a)))
(def c (reaction (+ @b 10)))
(defn view-c []
  (prn "Rendering view-c") [:div
  [:div @c]
  [:button {:on-click (fn [e] (swap! a update :x inc))} "inc x"]
  [:button {:on-click (fn [e] (swap! a update :y inc))} "inc y"]])
(defcard-rg reaction-example [view-c])

Реакции — это очень лаконичный способ выразить поток данных. Здесь вы начинаете с ratom, содержащего значения x и y. Затем вы строите реакцию b, которая наблюдает только за значением x. Затем введите еще одну реакцию c, которая наблюдает за суммой b и 10. Затем создайте компонент, который реактивно визуализирует c. Обратите внимание, что когда вы нажимаете кнопку «inc x», представление обновляется с результатом применения выражений. При нажатии кнопки «inc y» ничего не происходит. Проверьте консоль, чтобы убедиться, что сообщение «Rendering view-c» печатается только при нажатии «inc x». Это очень хорошо, потому что представление никак не зависит от y. Если бы вы разыграли a вместо c в представлении, оно было бы перерисовано, даже если y изменилось. Реагент реагирует на реакции и ратомы через requestAnimationFrame. Таким образом, изменение многих ратомов и зависимых от них реакций приводит только к одной фазе рендеринга.

person Timothy Pratley    schedule 11.07.2016
comment
Где-нибудь я могу прочитать больше о «реакции»? Документации по API немного не хватает, и она не упоминается во вводном руководстве! Фактически, я нашел это для многих частей API. - person Boyentenbi; 14.07.2016
comment
Именно, я пытаюсь сделать так, чтобы каждый ввод на моей странице реагировал на каждый ввод, кроме самого себя. Я думаю, что могу добиться этого с помощью макроса реакции, но не знаю, как! - person Boyentenbi; 14.07.2016
comment
github.com/Day8/re-frame#how-flow- происходит-в-реагенте ‹-- это хороший отчет по reaction с включенным примером. - person Timothy Pratley; 14.07.2016
comment
Я добавил в ответ пример, относящийся к reaction, надеюсь, это поможет... дайте мне знать, если неясно. - person Timothy Pratley; 14.07.2016
comment
Это имеет смысл, ура. У меня все еще есть проблемы, но я думаю, что лучше начать новый вопрос. - person Boyentenbi; 15.07.2016
comment
Новая ссылка на статью «Как происходит поток в реагенте»: day8.github.io/re-frame/ механика потока - person Eric Auld; 08.03.2021