I have an text input element that uses component state and application state.
In the example shown in React: More About Refs, the goal is to call focus on the element after re-rendering. Here is the key part, done with JS in React.
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!
});
},
I want to do something similar with Om. I've noticed neither
Om's
set-state!(for changing component state; see the docs and source), norOm's
update!(for changing application state; see the docs and source)
provides ways to specify a callback. So I'm looking for other ways to cause something to happen later, after a re-render.
Here's my example:
(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')))))
(Note: parse-int, not shown, "cleans up" the component state so that it is suitable for the application state.)
Changing the text input's component state doesn't cause it to lose focus, but mutating the application state does.
I've tried using core.async channels, but that does not seem to help because I only want the callback to happen after the re-render has completed.
I also tried using IDidUpdate, like this:
(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)))
; ...
)))
Update: The IDidUpdate lifecycle event does fire if only the component state is updated. However, it does not fire if the application state changes (due to om/update!, above).
IDidUpdatedidn't fire. It won't fire after the initial render of course, so I'm assuming that you mean it doesn't fire after the input element has been modified. - skillet-thiefIDidUpdatefiring after changes to component state. However, in my case, it does not after changes to application state; this makes me think that if a component is destroyed and recreated,IDidUpdatedoes not fire. - David J.IDidMountinstead. - skillet-thiefrefagain.IDidUpdate/IDidMountprobably won't quite do the same thing they are doing in the React example with ref. To follow their example, you would have to use ref in yourhandle-changefunction, accessing the ref property ofownerand then setting the focus through the owner object. If I understand the docs correctly,refbasically just gives you access to the React component in a context that is outside of the usual lifecycle. Interesting stuff! - skillet-thief