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

问题描述:

我有一个使用组件状态和应用程序状态的文本输入元素。渲染后的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. 嗡的set-state!(改变组件的状态;看到docssource),也不

  2. 嗡的update!(改变应用程序的状态;看到docssource

提供了指定回调的方法。因此,我正在寻找其他方法,以便在重新渲染后在稍后发生某些事情。

这是我的例子:

(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频道,但似乎没有帮助,因为我只想在重新呈现完成后发生回调。

(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,这样的尝试的IDidUpdate生命周期事件确实火如果只有部分状态更新。但是,如果应用程序状态更改(由于上述om/update!),它不会触发。

+0

我很好奇,为什么'IDidUpdate'不火的通道。在初始渲染之后它不会触发,所以我假定你的意思是在输入元素被修改之后它不会触发。 – 2014-09-21 19:23:33

+0

我刚刚更新了最后一段:在修改组件状态后,我确实看到了'IDidUpdate'触发。但是,就我而言,它并没有在应用程序状态发生变化之后;这让我觉得如果一个组件被销毁并重新创建,那么'IDidUpdate'不会触发。 – 2014-09-21 20:21:15

+0

这可能是发生了什么事。如果某个组件被销毁,那么正在安装一个新组件。在这种情况下,您需要'IDidMount'来代替。 – 2014-09-22 01:21:37

一个潜在的解决方案就是在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}))}) 

然后 - 在组件代码,您可以订阅山的变化,并关闭在卸载

... 
    (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)}) 
     ... 
    )))