Fork me on GitHub

Hey there, I'm new to both React and reagent and I'm looking for a way to prevent a component from re-rendering while it is in focus. What is the recommended way to do something like that?


It should be possible to specify this function using reagent.core/create-class:


So it would always return false when something in the component is in focus. But TBH, what you're describing something rather suspicious. My intuition is that it would be better to track "component is focused" as a part of your app's state and make explicit decision at the component's parent level whether to send in the new props or not, or something like that.


Initially, I wanted to do that, but I couldn't get it to work. The function was simply never called. Then, I read that this should only be used for performance optimisations, so I thought I'd ask elsewhere first to see if there's a better way and before I waste my time on this.


Hmm, trying to do this via the parent seems like a good idea. I'm not sure if this'll work in my case, but I can try


For context, what I have is essentially an editor for some structured data, and multiple components that represent different ways to edit it (and they should all be synced with the data). However I don't want to update the text editor component while a user is typing because that leads to weird trip-ups


Ah! Then just change the relevant data only when the focus is lost!


Keep the internal state in an internal ratom, update the external one once the component loses focus or a user presses Enter.


I want the other components to update while typing though


Also, thinking about using props and deciding it in the parent, it would still be re-rendered either way, since I can't just say "use the component you had before", right?


Oh, so you mean that if you enter something in a text area, you don't want that very same text area to receive the newly entered data because it messes with up the text cursor?


Right, yeah, and this editor component also auto-formats which doesn't really work live


Or like, when someone is typing and the value gets replaced simultaneously


Alright. What I describe specifically is a problem of text input components in Reagent. Or, rather, in React in general if the component is a controlled one. Some libraries fix it, some don't. Reagent fixes it for the regular HTML inputs, but e.g. for a Material-UI text input I had to fix it myself by IIRC wrapping an uncontrolled component in a thin controlled wrapper. But I think you describe a different scenario. I think having two sets of ratoms would still solve it - you would dereference either the internal ratom or the external one, depending on whether the component is focused or not.


As I said, I don't really want to update only when focus is lost.


You can organize that with an appropriate ratom separation. I'm afraid your problem description is still too vague for me to write something more specific.


Well I had been thinking about each editor component having its own view of the data but I thought this would be really bad in terms of design because of redundancy and consistency. Also as soon as you introduce more ways to edit the data you run into the mediator problem


I'm sorry if I'm being vague, as I said I'm really new to this. I've never done proper frontend development before


This is the renderer for the component in question, if it helps at all. commands is the shared data

(fn [format]
  [:> ace-editor
   {:mode (ace-mode format)
    :width ""
    :height ""
    :class "min-h-full rounded border-2 border-gray-400 border-b-1 w-full"
    :value (con/write-str format @commands)
    :on-change (fn [text _] (reset! commands (con/read-str format text)))
    :on-blur (fn [_ _] (reset! focused false))
    :on-focus (fn [_] (reset! focused true))}])


Ignore the on-blur and on-focus handlers, these were from my previous attempts


That's alright. If you still think that there's something good in that idea, you should come up with the simplest example that would demonstrate it, and then write out the desired scenario. Something like "there are two text inputs on a page; the user types a sentence in the first field - the second field gets updated with the result of a function called on the contents of the first input; user types something in the second input field - something else happens". I'm afraid your code doesn't help me at all because I still have no idea what the usage scenario is. What the, possibly imaginary, state machine is - what the states are, what the transitions are.


All editor components share the same data, they just display it differently and allow you to edit it differently. To the left there is a visual representation of the data that can be edited through a GUI. To the right there is the textual representation of the data which can be edited like in a text editor. When I add or change something in either, the other is updated as well.


That's really all there is to it


> weird trip-ups > this editor component also auto-formats which doesn't really work live > when someone is typing and the value gets replaced simultaneously So far it all sounds like it's a fundamental problem with that ace-editor and not with how you use it.


Or maybe it's your (con/write-str format ...) - if so, then just don't use that function until the editor is out of focus.


I don't think this is an issue with specifically this editor. This is an inherent issue if you replace the content while typing and reset the cursor position


The problem is that changes - even though they are already reflected in the editor - cause the component to re-render.


> Or maybe it's your (con/write-str format ...) - if so, then just don't use that function until the editor is out of focus. Also this sounds great but it will still be re-rendered regardless if I deref the data atom anywhere in the renderer + I would need to keep track of the entire editor's state and restore it every time an unnecessary update is triggered. This just seems really bad.


I think my best bet is probably still shouldComponentRender(). So I'll just ask about that instead:

(defn test-comp []
   {:display-name "test-comp"
    :reagent-render (fn [] [:strong (pr-str @commands)])
    :should-component-update (fn [_ _ _] (println "NO IT SHOULDNT") false)}))
I have this component as a test and I'm wondering what's wrong with it, because the :should-component-update function never seems to be called and it always re-renders.


> it will still be re-rendered What is the exact issue with just re-rendering itself? > I would need to keep track of the entire editor's state and restore it every time an unnecessary update is triggered. This just seems really bad. Not the entire state - just the one you feed it from the outside.


> What is the exact issue with just re-rendering itself? Redundancy + auto-formatting


Also I'd have to store other state that I shouldn't have to care about like cursor position


Not sure why :should-component-update isn't called, but in Reagent source code it's conditioned on reagent.impl.util/*always-update*.


Should I disable that or...? I feel like this should be documented, no?


Setting it to false doesn't seem to have an effect anyway


No clue whatsoever, sorry.


Hmm. I might make a minimal app that uses that lifecycle method and even open a GitHub issue if it doesn't work at all


The documentation on this needs to be updated anyway, they call this method component-should-update in the tutorial


how often does the value the editor shows change when it's not focused?


I was working on a problem just like this a couple days ago


what I did is have the editor component be "uncontrolled" - i.e. it doesn't have its :value controlled by the ratom


and then specific actions would force the editor component to remount


here's sort of what it looked like:

(r/with-let [editor-inst (r/atom 0)
             editor-value (r/atom "")]
   [editor {:key @editor-inst
            :initial-value @editor-value
            :on-change #(reset! editor-value %)}]
   [:button #{:on-click (fn []
                          (reset! editor-value "")
                          (swap! editor-inst inc))}


so the editor would ignore any changes to the value of editor-value , whether it was made by the editor itself or outside. but if you click the "Clear" button, it would change the editor-value and force the editor component to reinitialize with the new value by changing the value passed to its :key prop


:key is used by React to determine whether it should maintain the same component instance between renders, you can use this strategy with any component or element to force a remount


This is an interesting approach, hmm


What still doesn't sit right with me is the additional coupling this introduces. Idk if that's just me


What I mean is, like, these components should be independent. The only thing they should share and care about is the data atom. It's a bit frustrating to me that there seems to be no "pretty" way to achieve this


handling text input is tricky because there's so much essential complexity


most elements on the page we can just blow away and redraw, but text inputs & editors have a ton of internal state - cursor position, text selection, etc.


that's fair, yeah.


Is there any chance you know anything about should-component-update?


forms are another place where this "let the DOM take the wheel" approach works better IME


Trying to weigh my options here


let me see... just to make sure, what version of reagent are you using?


I'm seeing similar behavior... it's not calling the fn I pass to :should-component-update when I change a ratom


I do see it called when you pass in new props


here's a working example:

(defn test-comp
   {:display-name "test-comp"
    :reagent-render (fn [commands] [:strong (pr-str commands)])
    :should-component-update (fn [_ _ _] (println "NO IT SHOULDNT") false)}))

(defn app
  [test-comp @commands])


this actually matches my expectations from pure React. shouldComponentUpdate is only called when new props are passed in, not when internal state changes


the confusing part is that in this case it's not internal state changing but an external reagent atom. but Reagent updates the component by calling an internal setState, which is why it bypasses shouldComponentUpdate


That makes sense, yeah