Comments (23)
A similar request:
https://groups.google.com/forum/#!topic/clojurescript/rGcBOvLOhm4
from reagent.
For the record this is my current solution:
(defn main
[] ;; could be [x y z] here
(let [some (local but shared state) ;; <--- closed over by all lifecycle fns
can (go here)]
(reagent/create-class
{:component-did-mount
#(println "main: component-DID-mount")
:component-will-mount
#(println "main: component-WILL-mount")
:render
(fn [this] ;; unfortunately, real params not supplied.
(let [[_ x y z] (reagent/argv this)] ;; Ugly: get real params. Ignore the first
[:div
nil
(str x " " y " " z)]))})))
(reagent/render-component
[main 1 2 3] ;; pass in x y z
(.-body js/document))
Kinda okay, except for the ugly step in accessing reagent/argv
.
from reagent.
A neater solution (at the uncomfortable expense of using an internal implementation detail):
(defn main
[] ;; could be [x y z] here
(let [some (local but shared state) ;; <--- closed over by all lifecycle fns
can (go here)]
(reagent/create-class
{:component-did-mount
#(println "main: component-DID-mount")
:component-will-mount
#(println "main: component-WILL-mount")
:component-function ;; notice: using ':component-function' instead of ':render'
(fn [x y z] ;; correct parameters - nice
[:div
nil
(str x " " y " " z)]))}))
(reagent/render-component
[main 1 2 3] ;; pass in x y z
(.-body js/document))
@holmsand Any chance we could de-risk this approach by making :component-function
officially part of the reagent API? (but perhaps with a more render-y name, like :reagent-render
-- if so renames required here and here).
What we have above is so neat, and feels so right, compared to the with-meta
approach which to me seems:
- messy - code for one component spread across multiple functions
- can't handle the scenario originally raised by @ptaoussanis in this issue.
from reagent.
@mike-thompson-day8 I completely agree that :component-function
should be documented (and probably also this "return results from create-class from render trick"). It is very elegant, and I didn't even know you could do it until you brought it up...
I added a couple of tests for the trick (and :component-function
) in testreagent.cljs, to make sure that it keeps working.
About that name: I've been calling "functions that become components when called using [fun args]
in Hiccup forms" for "component functions" in the docs, so at least :component-function
is consistent with that.
An alternative/complementary approach to the "return-create-class-trick" to make sharing local state between different lifecycle methods easier is to expose the atom
that supports set-state
etc. You could get at that atom by calling, say, (r/state-atom this)
or (r/state-atom (r/current-component))
.
Any thoughts on that?
from reagent.
I don't think that's ((r/state-atom this)
or (r/state-atom this)
) very elegant. I think Reagent should strive for functional solution, that is, state in closures, or at least access to atom via closure (and across life-cycle functions). That means, both rendering and life-cycle functions, should have access to the same vars via closure, be it atoms or whatever else.
from reagent.
I'd too would vote for the "return-create-class-trick" (as shown above).
from reagent.
π return-create-class-trick
Would it be possible/expensive to create classes on the fly each time one of :component-did-X
is present in a component attributes?
;; could this...
(defn my-div
[_]
(reagent/create-class
{:component-did-mount #(println "main: component-DID-mount")
:component-will-mount #(println "main: component-WILL-mount")
:component-function (fn [_]
[:div {:style {:border "1px solid black"}}
"some div"])}))
;; become this?
(defn my-div [_]
[:div {:style {:border "1px solid black"}
:component-did-mount #(println "main: component-DID-mount")
:component-will-mount #(println "main: component-WILL-mount")}
"Some div..."])
from reagent.
@Frozenlock I'm not keen to see React
lifecycle functions mixed in with style
. I'd prefer to see the two worlds separate.
from reagent.
style
is one level nested into the attributes map, which already filters out unknown/incompatible items when rendering to DOM. (I learned that the hard way when some SVG properties were silently thrown out.)
Maybe it's because I've used the metadata approach up until now, but I've always found the lifecycle functions to be a hassle. At least in comparison to everything else which pretty much just works.
I think of it this way: what if the attributes were metadata up until now?
(defn my-div [a]
[:div (str "my div :" a)])
(with-meta
my-div {:style {:border "1px solid black"}}) ;; boooo!
;;; but hiccup had the brilliant idea of an optional
;;; attributes (properties) map
(defn my-div [a]
[:div {:style {:border "1px solid black"}}
(str "my div :" a)]) ;; yeah!
Anyways, I don't use the lifecycle functions enough to have any strong feelings about it.
from reagent.
I also find that :component-function
is not the best name. :component-render
would be much more descriptive and understandable.
from reagent.
I've created a Wiki page, so we have an explanatory resource to point to when this issue comes up again: https://github.com/reagent-project/reagent/wiki/Creating-Components
Any edits or clarifications on that Wiki page are most welcome.
from reagent.
Great wiki page @mike-thompson-day8 but it makes me wonder whether adding :component-render
as an alias for :component-function
(and perhaps deprecating the latter) would make usage more obvious.
In fact, why not use create-component
and have it use aliases for all the lifecycle methods that drop the component-
prefix? It would be less typing than the current approach and more consistent (especially since you are creating a component not a class?).
from reagent.
Doesn't :component-render
risk getting confusing? After all, :render
renders a component as well...
How about :function-render
, to (hopefully) highlight that this thing is called using "function conventions", rather than the boring old React way?
Or :fn-render
? Or something better...?
from reagent.
π :function-render
(but i think any of the suggestion are an improvement over :component-function
)
from reagent.
Well, anything with function
in it is a face palm. Let's see, what about...
(defn my-component
[x y z]
(let [some (local but shared state)
can (go here)]
(reagent/create-class
{:did-mount #(println "Did mount")
:will-mount #(println "Will mount")
:render
(fn [x y z]
[:div (str x " " y " " z)]))}))
Because, component-
prefixes are pointless. I also think that the argument repetition is a problem.
from reagent.
I'm suggesting mapping from "Reagent keys" which we choose and control to "React keys" rather than just slavishly following the React lifecycle names as keys.
As noted on @mike-thompson-day8 wiki page:
"Its a trap to mistakenly use :render
because you won't get any errors, except the function you supply will only ever be called with one parameter, and it won't be the one you expect."
I think we can remove that mistake by making :render
do the right thing by having create-class
(or, better, create-component
) map :render
to what users would intuitively expect, as @skrat shows in an example.
Having to repeatedly type :component-
for no good reason seems counter to the otherwise simple and elegant approach Reagent takes elsewhere.
from reagent.
@skrat and @seancorfield Naming is hard :)
I agree that :render
should have been the one with Reagent-style arguments, but I think it would be a bit nasty to change it now (without any way of issuing a warning).
As you say @seancorfield, switching to something like create-component
with a new argument for :render
is probably the way forward (create-class
could then be deprecated with a suitable warning).
In the meantime, the-thing-that-is-now-component-function must be called something, and since there seems to be universal hate for the previous alternatives, how about these:
:reagent-render
(at least it sort of says what it does...):run
(short and descriptive, but maybe differs too much from the normal React terminology?)
That would maybe be a temporary solution anyway, if we go with create-component
or some such, but it would sure be nice to get a 0.5.0 out of the door :)
from reagent.
+1 for reagent-render
from reagent.
Reagent 0.5.0-alpha3 now supports :reagent-render
. I still think that create-component
is a good idea, though, but after 0.5.0.
from reagent.
I think this issue can be closed
from reagent.
I kinda like the with-meta
syntax better. Any reason why a change can't be made to allow it on inner components? It seems a bit inconsistent to use one method when outer and another when inner (and the reasons for this glaring inconsistency will not be immediately obvious on a new reader's first encounter with a reagent project). Given this, the only responsible way forward is to avoid with-meta
entirely and explicitly use create-class
everywhere.
from reagent.
Actually, I believe this should still work if you wrap the inner component with a vector:
(fn outer-component [arg1 arg2]
(let [local-state (atom {})] ; Perform setup, etc.
[(with-meta
(fn inner-component [arg1 arg2]
[:div (str arg1 "," arg2 "," @local-state)])
{:component-did-mount
(fn [this]
;; Has access to local-state (but doesn't currently trigger)
)})]))
from reagent.
Actually, I believe this should still work if you wrap the inner component with a vector:
(fn outer-component [arg1 arg2] (let [local-state (atom {})] ; Perform setup, etc. [(with-meta (fn inner-component [arg1 arg2] [:div (str arg1 "," arg2 "," @local-state)]) {:component-did-mount (fn [this] ;; Has access to local-state (but doesn't currently trigger) )})]))
I have try your answer, actually, pass arg1
arg2
to inner-component
will make them undefined. If want work, we should do:
(fn outer-component [arg1 arg2]
(let [local-state (atom {})] ; Perform setup, etc.
[(with-meta
(fn inner-component []
[:div (str arg1 "," arg2 "," @local-state)])
{:component-did-mount
(fn [this]
;; Has access to local-state (but doesn't currently trigger)
)})]))
But this is against with Form-2
that how to create a component, which is recommended by officials'.
from reagent.
Related Issues (20)
- Bad react key warning when using false as key
- examples/material-ui not work HOT 1
- examples/material-ui not work HOT 1
- Docs on βChanged?β inaccurate? HOT 5
- React 18 does not live reload with Shadow-CLJS in non-trivial projects HOT 5
- Prop types
- `TypeError` when using function components with `r/create-class` with `dom.server/render-to-string`
- ReactDOM.render is no longer supported in React 18. Use createRoot instead. HOT 2
- shadow-cljs emits a warning when using with-let HOT 4
- Korean input broken on :input and :textarea HOT 1
- Update docs? HOT 1
- MUI ThemeProvider not working HOT 1
- @@ -62,6 +62,45 @@ class _Config { } } // eslint-disable-next-line valid-jsdoc /** * fetch problem title, level via solved.ac api * @see {@link https://solvedac.github.io/unofficial-documentation/#/operations/getProblemByIdArray} * @param {string[]} pids - list of problem id (up to 100) * @param { ({ problemId, titleKo, level }) => void } callback */ function fetchProblemsFromSolvedAc(pids, callback) { const query = encodeURIComponent(pids.join(',')); console.log(`https://solved.ac/api/v3/problem/lookup?problemIds=${query}`); fetch(`https://solved.ac/api/v3/problem/lookup?problemIds=${query}`) .then((res) => { console.log('fetchProblemsFromSolvedAc', res); return res; }) .then((res) => res.json()) .then(callback) .catch(() => callback(null)); }
- Readme says npm i or add deps to .edn but (in my case at least) needed both HOT 1
- Any interest in being able to replace `react/createElement` with a custom function? HOT 7
- The cursor disappears at the end of a controlled input on Chrome
- Reusable components following HTML semantics of Opional Attributes and Variadic Children HOT 3
- Controlled input loses cursor under ShadowRoot in React 18 HOT 3
- snake_case mentioned instead kebab-case in documentation of create-class function.
- Class name composition is harder than it needs to be
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
π Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. πππ
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google β€οΈ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from reagent.