渲染后的Om回调(在更改应用程序状态后聚焦元素)

时间:2014-09-21 07:03:55

标签: reactjs clojurescript om

我有一个使用组件状态和应用程序状态的文本输入元素。

React: More About Refs中显示的示例中,目标是在重新渲染后在元素上调用focus。这是关键部分,在React中使用JS完成。

clearAndFocusInput: function() {
  // Clear the input
  this.setState({userInput: ''}, function() {
    // This code executes after the component is re-rendered
    this.refs.theInput.getDOMNode().focus();   // Boom! Focused!
  });
},

我想用Om做类似的事情。我没有注意到

  1. Om set-state!(用于更改组件状态;请参阅docssource),

  2. Om update!(用于更改申请状态;请参阅docssource

  3. 提供了指定回调的方法。因此,在重新渲染之后,我正在寻找其他方法来导致某些事情发生。

    这是我的榜样:

    (defn input-component
      [{:keys [app-state-key class-name]}]
      (fn [data owner]
        (reify
          om/IInitState
          (init-state
            [this]
            {:text (data app-state-key)})
    
          om/IRenderState
          (render-state
            [this state]
              (let [handle-change (handle-change-fn data app-state-key)]
                (dom/input
                  #js {:ref (name app-state-key)
                       :type "text"
                       :className class-name
                       :value (:text state)
                       :onChange #(handle-change % owner state)}))))))
    
    (defn handle-change-fn
      [app-state app-state-key]
      (fn [e owner state]
        (let [element (.-target e)
              value (.-value element)]
          (om/set-state! owner :text value)
          (if-let [value' (parse-int value)]
            (om/update! app-state app-state-key value')))))
    

    (注意:parse-int,未显示,"清除"组件状态,以便它适合应用程序状态。)

    更改文本输入的组件状态并不会导致它失去焦点,但会改变应用程序状态。

    我尝试过使用core.async频道,但这似乎没有帮助,因为我只希望在重新渲染完成后进行回调。

    我也尝试使用IDidUpdate,如下所示:

    (defn input-component
      [{:keys [app-state-key class-name]}]
      (fn [data owner]
        (reify
          ; ...
          om/IDidUpdate
          (did-update
            [this prev-props prev-state]
            (let [e (om/get-node (.-owner this))]
              (.log js/console "did-update" e)
              (.focus e)))
          ; ...
          )))
    

    更新:如果仅更新组件状态,则IDidUpdate生命周期事件触发。但是,如果应用程序状态发生更改,则不会触发(由于上面的om/update!)。

1 个答案:

答案 0 :(得分:0)

我的头脑中的一个可能的解决方案是在你的om / root调用中使用core.async和:tx-listen

:tx-listen选项允许您指定在提供的应用程序状态更改时要调用的函数 - 因此我们提供了一个函数,该函数写入附加了“pub”的通道。

然后,当您的组件安装完成后,您可以使用IWillMount订阅该频道上的活动,并在IWillUnmount上进行清理。

显然这里有一些清理空间(特别是,您可能希望缩小组件所听取的事件类型) - 但它表明了基本的想法。

....
(defonce changes (chan))
(defonce changes-pub (pub changes :msg-type))
....
(om/root
   (input-component {:app-state-key :input-state
                     :class-name "theClass"})
   app-state
   {:target (. js/document (getElementById "app"))
    :tx-listen (fn [tx-data root-cursor]
                 (put! changes {:msg-type :state-updated}))})

然后 - 在您的组件代码中,您可以订阅mount上的更改,并在卸载时关闭通道

   ...
   (reify    
      om/IWillMount
      (will-mount [_]
        (sub changes-pub :state-updated (:sub-chan (om/get-state owner)))
        (go-loop []
          (if-let [update (<! changes)]
            (let [node (om/get-node owner (name app-state-key))]
              (println "Resetting focus")
              (.focus node)
              (recur)))))  
      om/IWillUnmount
      (will-unmount [_]
        (close! (:sub-chan (om/get-state owner))))
      om/IInitState
      (init-state 
        [this]
        {:text (data app-state-key)
         :sub-chan (chan)})
      ...
      )))