GithubHelp home page GithubHelp logo

Comments (23)

mike-thompson-day8 avatar mike-thompson-day8 commented on May 5, 2024

A similar request:
https://groups.google.com/forum/#!topic/clojurescript/rGcBOvLOhm4

from reagent.

mike-thompson-day8 avatar mike-thompson-day8 commented on May 5, 2024

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.

mike-thompson-day8 avatar mike-thompson-day8 commented on May 5, 2024

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:

  1. messy - code for one component spread across multiple functions
  2. can't handle the scenario originally raised by @ptaoussanis in this issue.

from reagent.

holmsand avatar holmsand commented on May 5, 2024

@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.

skrat avatar skrat commented on May 5, 2024

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.

mike-thompson-day8 avatar mike-thompson-day8 commented on May 5, 2024

I'd too would vote for the "return-create-class-trick" (as shown above).

from reagent.

Frozenlock avatar Frozenlock commented on May 5, 2024

πŸ‘ 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.

mike-thompson-day8 avatar mike-thompson-day8 commented on May 5, 2024

@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.

Frozenlock avatar Frozenlock commented on May 5, 2024

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.

ilyabo avatar ilyabo commented on May 5, 2024

I also find that :component-function is not the best name. :component-render would be much more descriptive and understandable.

from reagent.

mike-thompson-day8 avatar mike-thompson-day8 commented on May 5, 2024

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.

seancorfield avatar seancorfield commented on May 5, 2024

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.

holmsand avatar holmsand commented on May 5, 2024

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.

gadfly361 avatar gadfly361 commented on May 5, 2024

πŸ‘ :function-render (but i think any of the suggestion are an improvement over :component-function)

from reagent.

skrat avatar skrat commented on May 5, 2024

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.

seancorfield avatar seancorfield commented on May 5, 2024

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.

holmsand avatar holmsand commented on May 5, 2024

@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.

mike-thompson-day8 avatar mike-thompson-day8 commented on May 5, 2024

+1 for reagent-render

from reagent.

holmsand avatar holmsand commented on May 5, 2024

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.

mike-thompson-day8 avatar mike-thompson-day8 commented on May 5, 2024

I think this issue can be closed

from reagent.

johanatan avatar johanatan commented on May 5, 2024

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.

yogthos avatar yogthos commented on May 5, 2024

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.

kangbb avatar kangbb commented on May 5, 2024

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)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo 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.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.