I am trying to build a block-based richtext editor similar to Notion (with far less features of course) for a project. To get a feel for how it can be done, I adapted this tutorial to ClojureScript with Reagent: Link to GitHub Repo
Since I am still somewhat new to ClojureScript and Reagent (and also never did anything serious with React yet), I appreciate any feedback to improve my adaptation and make it more idiomatic to how Reagent works vs pure React (keep in mind that to keep things simple I do not want to concern myself with re-frame yet).
There is one question I am trying to answer:
In React, you often use a callback function in setState
for things you need to do after a state change. As I understand, the callback is triggered after the re-render occurred due to the state change, which is why the author of the original tutorial was able to set the caret position.
Here, I have used reagent/after-render
instead, which works, although the behaviour is not quite the same (which is why I had to put it after all state updates instead of just the one in tag-selection-handler
as it was with the callback in the pure React code)
I could not find much documentation on reagent/after-render
, despite its apparent usefulness. Is there a more idiomatic way to react to state changes in ratoms?
I also made some observations about the use of life-cycle methods in Reagent, maybe someone has alternative suggestions to my approaches here:
I have the impression that, because of ratoms, life-cycle methods have much less use in Reagent. For example, you can initialize internal state in the outer let-binding of form 2/3 components instead, so there is no need for :component-did-mount
.
In :component-did-update
, the previous state is not available in Reagent, since (please correct me, if Iâm wrong) it doesnât even use this.state
.
So I found that sometimes (e.g. for the update to match-sorter in (select-menu) in my editor adaptation) it makes more sense to update state in event handlers where you actually have access to the previous state and use reagent/after-render
to react to the state change, so you can check if it actually changed to not trigger an unneccessary re-render.
When passing changes to the parent state (e.g. to update the *blocks ratom of the parent every time the child component re-rendered), it is important to check for differences to prevent an infinite update cycle. I found that with Reagent, it makes more sense to compare differences to the parent state instead of keeping a previous state of the child component around.
Sorry for the long post, I hope it was clear enough.