Open alidcast opened 5 years ago
The primary problem is still knowing whether the performance impact of memoizing the calculation is less than the performance impact of running the render function again.
One thing I do have on an alpha branch are some macros that will automatically detect what dependencies to add to the array we give to React's useMemo
/ useEffect
/ etc. Something like:
(use-memo
[foo] ;; <-- normal use, passing in a vector of deps to give to React for memoizing the calculation
(+ foo 1))
(use-memo
:auto-deps ;; <-- tells macro to detect and add dependencies in the calculation automatically
(+ foo 1)) ;; <-- macro will detect that `foo` is used in this and add it to the deps passed to React
Re: mobx. Mobx works similar to Reagent, and the way they are able to optimize renders is by tracking state outside of the render tree. The same thing can be done with hx, but it comes with some drawbacks. The pure React way focuses on keeping state within the render tree and aggressively annotating our components with memo when needed, I don't think hx
can get around that yet.
I see, thanks for clarifying.
the biggest issue imo is not one-off declarations / nesting props but trying to use Context API for global state management, since all consuming components render for on any state change, regardless of whether their using the state. and it'd be great not to have to always use use-memo
when consuming a context provider
maybe like the use-memo :auto-deps
, there can also be a create-context
utility for overcoming the above issue. react-tracked and constate are examples I've been looking at
I've thought about this some more and I like what you're thinking about re: Context.
I think that this should still be opt-in, but can be made much more ergonomic with some syntax sugar.
What I'm currently playing with in my head is annotating expressions with metadata, which the defnc
macro could then inspect and expand into a call to use-memo
or use-callback
. For instance:
(defnc MyComponent []
(let [state (hooks/useContext global-state-context)
foo (:foo state)]
^:memo [:div foo]))
Which would be expanded to:
(defnc MyComponent []
(let [state (hooks/useContext global-state-context)
foo (:foo state)]
(hooks/useMemo
(fn [] [:div foo])
[foo])))
Hey @Lokeh JS dev here, just started learning Clojure. I appreciate your work in integrating JS/react tools, and just now read your "when-is-immutability-fast" article.
Regarding your "wrapping all of our components in React.memo" comment in the article, I also thought about this when learning about macros. I like the idea of taking advantage of Clojure so that we can stop thinking about performance and focus on the UI. The concern seems to be that not every component needs this optimization - but wouldn't it be possible to track dependencies, and only apply the
useMemo
hook where it's necessary?JS tools like
mobx
provide this rendering magic viaproxies
, but since macros give control over code+data this optimization could be done via react itself without having to add another dependency.Just started learning Clojure/macros of course, so this is all high level for me still, would be great to hear your thoughts! Thanks