атом / компонент реагента повторно визуализируется только один раз

Я создал систему сносок, используя реагент (для всех, кто читает javascript, это оболочка clojurescript поверх реакции), построенную на основе семантического пользовательского интерфейса (только css), который обрабатывает щелчки и события клавиатуры, а также наведение курсора мыши. И все работает отлично, за исключением одной мелочи: он правильно обрабатывает события клавиатуры только один раз, а затем дает сбой.

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

Все атомы являются реактивными атомами. Точкой входа является footnote, который представляет собой компонент, который принимает текст сноски, а также маркер страницы (что не имеет значения).

footnote и его последующие функции:

  1. взять значение счетчика из глобального состояния, увеличить его и использовать как число для сноски,

  2. создать некоторое локальное состояние (атом реагента), чтобы контролировать, виден ли модальный файл, содержащий текст, и инициализировать его значением false,

  3. прикрепить к тексту надстрочный идентификатор сноски с всплывающей подсказкой (при наведении курсора мыши)

  4. прикрепить обработчик событий по щелчку к этому идентификатору сноски, который устанавливает модальный атом видимости в значение true, и

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

Затем, когда модальный атом видимости истинен:

  1. к глобальному окну прикреплен другой прослушиватель событий клавиатуры, который устанавливает для модального атома видимости значение false.

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

Я думаю, должно произойти:

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

Что происходит на самом деле:

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

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

Интересно, что повторный рендеринг всей виртуальной «страницы» (то есть компонента более высокого уровня, который содержит компонент сноски - это одностраничный сайт с виртуальными страницами в качестве компонентов реакции / реагента), либо путем нажатия кнопки «Обновить», либо просто путем рендеринга другой компонент «страницы», содержащий разные сноски / без сносок, затем повторно отображает исходный компонент «страница», кажется, сбрасывает любое нестабильное состояние, которое я получаю, и триггеры клавиатуры снова работают.

В связи с этим, у меня есть другие события клавиатуры, зависающие в глобальном окне, но ничего, что использует те же нажатия клавиш, что и сноски. В частности, у меня есть события клавиатуры, которые я использую для навигации между «страницами» (h для дома и т. Д.). Интересно, что если я закрою модальное окно с помощью одной из этих других клавиш, он будет работать нормально - вполне возможно, потому что другой key сначала повторно отображает одну из других страниц, а затем отображает исходную страницу в соответствии с моим принудительным повторным отображением?

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

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

Наконец, я попытался включить reagent/force-update-all в close-modal, а также трюк "forcer" из этой проблемы. Ни то, ни другое не привело ни к каким изменениям в поведении.

Другая, возможно, относящаяся к делу информация:

Функция listen взята из библиотеки закрытия Google (goog.events), а не из простого javascript. Не знаю, есть ли у этого странная семантика или что-то, что может вызвать эту проблему.

У кого-нибудь есть блестящие идеи? Спасибо! :-)

(defn footnote-flag [num ratom text]
  [:sup {:data-tooltip text
         :on-click #(reset! ratom true)}
         (str "(" num ") ")])

(defn close-modal [ratom page]
  (cond
    (= @ratom true)
    (do
      (reset! ratom false)
      (navload page))))

(defn modal-content [text ratom page]
  (when @ratom
    (do
      (listen js/window "keypress" #(close-modal ratom page))
      [:span.ui.dimmer.modals.page.transition.visible.active
       {:on-click #(close-modal ratom page)}
       [:span.ui.standard.basic.modal.transition.visible.active.scrolling
        {:on-click #(close-modal ratom page)}
        [:p text]]])))

(defn handle-footnote-key [key-event num page ratom]
  (let [keypress (.-keyCode key-event)]
    (cond
      (and  (= (+ 48 num) keypress) (= page @stdio.nav.curpage))
      (reset! ratom true))))

(def footnote-counter (atom 0))

(defn footnote [text page]
  (do
    (swap! footnote-counter inc)
    (let [modal-state (atom false)
          num @footnote-counter]
      (listen js/window "keypress" #(handle-footnote-key % num page modal-state))
      (fn [text]
        [:span
         [footnote-flag num modal-state text]
         [modal-content text modal-state page]]))))

person Paul Gowder    schedule 03.07.2016    source источник
comment
Я примерно на 70% уверен, что проблема в listen в modal-content и footnote. При первом рендеринге footnote регистрирует слушателя для открытия модального окна при определенном нажатии клавиши. Затем, когда модальное окно зарегистрировано, оно настроено на закрытие модального окна при любом нажатии клавиши. Итак, после того, как модальное окно было отрисовано в первый раз, вы одновременно открываете и закрываете модальное окно. Я бы посмотрел на create-class, чтобы убедиться, что вы одновременно создаете и удаляете своих слушателей.   -  person Walton Hoops    schedule 04.07.2016
comment
Думаю, это правильно. После того, как вы наткнулись на решение ниже, это действительно больше похоже на проблему JavaScript / dom, чем на что-либо еще, когда все слушатели борются друг с другом. Я предполагаю, что случайное решение решило проблему, повторно подвесив слушателей событий (??) при повторном рендеринге или что-то в этом роде ...   -  person Paul Gowder    schedule 04.07.2016
comment
Я не знал, что вы можете удалить слушателя в create-class. Это, наверное, было бы чище!   -  person Paul Gowder    schedule 04.07.2016


Ответы (2)


Является ли footnote-counter clojure.core/atom, когда он должен быть reagent.core/atom?

person Mike Thompson    schedule 03.07.2016
comment
Нет, все атомы реагента (я только что упомянул название) - person Paul Gowder; 04.07.2016
comment
Переместите num @footnote-counter в отверстие внутри рендера fn - person Mike Thompson; 04.07.2016
comment
нравится? gist.github.com/paultopia/f299d3e41ee0a5ada462fbb85c757899 (эта сноска, к сожалению, ломает все они отображаются как 0 по какой-то загадочной причине, когда я делаю это таким образом) - person Paul Gowder; 04.07.2016
comment
Ага! Но это дало мне ключ к разгадке, и некоторые поиски пришли к решению! Добавляю свой собственный ответ, но также хотел бы получить некоторое представление о том, почему ... - person Paul Gowder; 04.07.2016
comment
Для протокола, я думаю, что это должен был быть комментарий, а не ответ, Майк, хотя, если бы это было правильно, вы могли бы сделать это ответом. Во всяком случае, это мой подход. (На данный момент, учитывая, что к этому примыкает обсуждение, я не думаю, что вам следует что-либо делать.) - person Mars; 16.07.2016

После некоторых поисков и подсказок Майка Томпсона, приведенных выше, решение представилось.

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

(defn footnote [text page]
  (do
    (swap! footnote-counter inc)
    (let [modal-state (atom false)
          num @footnote-counter]
      (fn [text]
        (do
          (listen js/window "keypress" #(handle-footnote-key % num page modal-state))
         [:span
          [footnote-flag num modal-state text]
          [modal-content text modal-state page]])))))
person Paul Gowder    schedule 04.07.2016