试剂原子/组分仅重新渲染一次

时间:2016-07-03 21:31:27

标签: reactjs clojurescript semantic-ui reagent

我创建了一个脚注系统,使用试剂(对于任何javascript人阅读,这是一个反应的clojurescript包装)建立在语义ui(仅限css)之上,它处理点击和键盘事件以及鼠标悬停。除了一件小事之外,一切都很完美:它只能正确处理键盘事件一次,之后就失败了。

代码位于底部,但它有点复杂,所以我会在展示它之前解释它。

所有原子都是试剂原子。入口点是footnote,它是一个获取脚注文本和页面标记(不相关)的组件。

footnote及其下游功能:

  1. 从全局状态获取一个计数器值,将其递增,并将其用作脚注的数字,

  2. 创建一些本地状态(试剂原子)来控制包含文本的模态是否可见,并将其初始化为false,

  3. 将带有工具提示的上标脚注标识符附加到文本中(用于鼠标悬停)

  4. 将一个点击事件处理程序附加到该脚注标识符,该标识符将模态可见性原子设置为true,并

  5. 将键盘事件侦听器附加到全局窗口,该窗口在按下数字时将模态可见性原子设置为true。

  6. 然后,当模态可见性原子为真时:

    1. 另一个键盘事件侦听器附加到全局窗口,它将模态可见性原子设置为false。

    2. 一个点击式监听器附加到模态本身(在一些地方,因为我吮吸DOM并且不相信我有能力找出可能会点击的位置),还要设置模态可见性原子为假。

    3. 我认为应该发生什么:

      在渲染时,鼠标悬停应显示带有文本的工具提示,单击脚注编号或按键盘上的脚注编号应打开带文本的模态。当模态打开时,单击任意位置应关闭它,就像按任意键一样。关闭模态后,用户应该可以通过单击或按键盘上的数字再次打开它。

      实际发生的事情:

      除了前一段中的斜体字之外的所有内容。相反,在关闭模态后,如果我按相应的键再次打开它,模态不会重新打开(单击仍然有效)。

      当我包装密钥处理函数调试日志调用时,它会将“true”记录到控制台,指示原子处于预期状态。

      有趣的是,重新渲染整个虚拟“页面”(即包含脚注组件的更高级别组件 - 这是一个单页网站,虚拟页面作为反应/试剂组件),可以通过点击刷新或只需渲染另一个包含不同/没有脚注的“页面”组件然后重新渲染原始的“页面”组件,似乎重置了我得到的任何类型的不稳定状态,键盘触发器再次工作。

      相关地,我在全局窗口上有其他键盘事件,但没有使用与脚注相同的键击。特别是,我有键盘事件,我用来在“页面”(h为家庭等)之间导航。有趣的是,如果我使用其中一个其他键关闭模态,它工作得很好 - 很可能是因为其他key首先重新渲染其中一个页面,然后根据我的强制重新渲染呈现原始页面?

      下面的代码(从我最初发布此问题时修改)试图通过重新渲染原始页面(即navload)来利用这最后一个怪癖,但没有骰子(可能因为反应足够聪明)如果它已经存在,不要重新渲染页面?)。

      我还尝试通过首先渲染一个虚拟页面然后立即再次渲染原始页面来强制重新渲染close函数,但是没有骰子 - 这实际上会使事情变得更糟(重新渲染不再重置键盘脚注容量。)

      最后,我尝试在reagent/force-update-all中添加close-modal以及在this issue中添加“forcer”技巧。没有导致任何行为改变。

      其他可能相关的信息:

      listen函数来自谷歌闭包库(goog.events),而不是普通的javascript。 Dunno,如果它有奇怪的语义或可能导致这个问题的东西。

      有没有人有任何精彩的见解?谢谢! : - )

      (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]]))))
      

2 个答案:

答案 0 :(得分:0)

footnote-counter clojure.core/atom应该是reagent.core/atom吗?

答案 1 :(得分:0)

经过一些探讨,并在Mike Thompson的上述提示的帮助下,出现了一个解决方案。

将键事件侦听器移动到返回的渲染函数而不是外部函数中似乎会生成正确的行为。我不太确定为什么,老实说,并且会喜欢任何洞察力......

(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]])))))