News for hackers. Stuff that matters.
Re:Thinking in React Hooks by pgt on Thursday December 12, 2019 @10:58AM (#21772910)
(Edit: it is worth noting that Hooks are based on Reagent's reactive atoms.)

Compare the complexity of Hooks with the simplicity of state management in [Reagent](http://reagent-project.github.io/) or [Rum](https://github.com/tonsky/rum).

Complete ClojureScript example using Reagent:

  (ns reagent-example.core
    (:require [reagent.core :as r])

  (def !count (atom 0)) ;; ! prefix is convention for state

  (defn counter-component []
    [:div
      [:p "You have clicked the button " @!count " times."]
      [:button {:on-click #(swap! !count inc)} "Increment"]])

  (r/render [counter-component] (js/document.getElementById "app"))
The same example using Rum:

  (ns rum-example.core
    (:require [rum.core :as rum])

  (def !count (atom 0))

  (rum/defc counter-component < rum/reactive []
    [:div
      [:p "You have clicked the button " (rum/react !count) " times."]
      [:button {:on-click #(swap! !count inc)} "Increment"]])

  (rum/mount (counter-component) (js/document.getElementById "app"))
Re:Thinking in React Hooks by lilactown on Thursday December 12, 2019 @11:33AM (#21773316)
I think that hooks and Reagent developed somewhat independently; or at least, they are not as related as one might think at first glance.

The primary difference is where the data _flows from_. Like another commenter pointed out, your examples can be done similarly as trivially with Hooks. The difference is that the state is stored in Reagent in a global mutable atom, while the state in React is stored in the React tree, and is tied to the render lifecycle.

This doesn't seem like a huge difference on its face, but in practice it makes for very different experiences navigating a code base.

In Reagent, your data is flowing down via props but also from the side via any number of atoms/subscriptions/etc. In React, you know that your data is always either state local to the component or passed via props.

This isn't even to mention the other various hooks like useEffect, which allows you to coordinate side-effects with changes in data in a composable, locally-scoped way that Reagent does not allow you to do. In order to coordinate side effects, it requires you to keep the description of side effects completely separate from your components, which makes them less composable.

For people who enjoy the way that React Hooks makes you think, and are also interested in ClojureScript (immutable data structures + amazing standard library + hot reloading + macros built into the language), check out https://github.com/Lokeh/hx or its WIP successor, https://github.com/Lokeh/helix (I am the author of these libraries).

Re:Thinking in React Hooks by filoeleven on Friday December 13, 2019 @11:30PM (#21787821)
I have a tangential question for you about helix. You switched from hiccup-style lists in hx to functions in helix.dom. I was just looking at fulcro and they made a similar choice. What drove that decision for you?

One of the things that first drew me to check out reagent and hx was that the components are declared as data. I haven’t tried any of these libs out yet, so while hiccup syntax looks to me to be more idiomatic clojure, there must be solid, practical reasons for implementing them as functions that I haven’t understood yet, and I’m curious to hear more about what they are.

Re:Thinking in React Hooks by lilactown on Monday December 16, 2019 @01:02PM (#21805283)
Performance, primarily. Hiccup that is generated _at runtime_ is incredibly wasteful; you allocate a bunch of immutable vectors and maps, then parse them into React elements and throw them away, every time your component is rendered.

This is a constant performance tax on your application and makes Reacts tradeoffs re: virtual DOM worse. React Elements are already data (they are POJOs), so you lose nothing in terms of declarative-ness using hiccup vs. functions/macros.

If you're very attracted to hiccup syntax, you can still use hx's hiccup parser with helix (docs incoming) or try out a library I wrote called thump[0], which uses reader tags to parse the hiccup at compile time into React Elements.

0: https://github.com/Lokeh/thump/

Re:Thinking in React Hooks by ng12 on Thursday December 12, 2019 @11:19AM (#21773189)
I don't think this is a compelling example. React would implement this almost identically:

    const Counter = () => {
      const [count, setCount] = React.useState(0)
      return (
        <div>
          <p>You have clicked the button {count} times.</p>
          <button onClick={() => setCount(count => count + 1)}>Increment</button>
        </div>
      )
    }
Replace the atom/swap with a useState hook and it's almost identical.
Re:Thinking in React Hooks by jmiskovic on Thursday December 12, 2019 @01:58PM (#21775017)
The difference is in how the state is handled. In Clojure, it is contained in an atom, which is a well documented language primitive which can change value in controlled manner. It's not some magic construct hidden behind innocently looking JS function useState().
Re:Thinking in React Hooks by dhucerbin on Thursday December 12, 2019 @02:32PM (#21775407)
To be fair, reagent uses it's own implementation that satisfy atom protocol. And it uses some funky magic with keeping derefs and triggering rerenders. In principle, as atom is idiomatic Clojure, setState is idiomatic javascript. ratoms and hooks both add some sugar to it.
Re:Thinking in React Hooks by pgt on Thursday December 12, 2019 @06:33PM (#21777714)
`setState(...)` is not idiomatic in JavaScript.
Re:Thinking in React Hooks by dhucerbin on Friday December 13, 2019 @03:26AM (#21779994)
I'm not sure how far back you want to go, but `this.set` is used in most frontend frameworks, starting around knockout and backbone. You had object which managed state for you (then it was model, today it is component) and it had some setter method.
Re:Thinking in React Hooks by ArchReaper on Thursday December 12, 2019 @06:36PM (#21777733)
React is open source, you can view the source for this "magic construct" so I don't see any validity to your argument.

useState returns 2 variables - a getter and a setter, where the getter is just a variable and not even a function to call.

How is that equatable to some hidden construct? How is Clojure superior to that?

Re:Thinking in React Hooks by mondaygreens on Thursday December 12, 2019 @11:27AM (#21773256)
Yeah that's way more readable and intuitive than anything that came before (that I'm aware of)!
Re:Thinking in React Hooks by kgwxd on Thursday December 12, 2019 @12:00PM (#21773637)
1 language vs 2 intertwined languages is compelling enough for me.
Re:Thinking in React Hooks by ng12 on Thursday December 12, 2019 @12:09PM (#21773740)
Do you mean JSX? Here you go:

    import {createElement as h} from "react"
    const Counter = () => {
      const [count, setCount] = React.useState(0)
      return h("div", {},
        h("p", {}, `You have clicked the button ${count} times`),
        h("button", {onClick: () => setCount(count => count + 1)}, "Increment")
      )
    }
There are additionally libs like hyperscript (https://github.com/mlmorg/react-hyperscript) that take the non-JSX approach further. Although honestly, I don't recommend it. JSX is really great even it seems a little unfamiliar.
Re:Thinking in React Hooks by ArchReaper on Thursday December 12, 2019 @06:33PM (#21777712)
What? JSX allows you to write HTML as though you are actually writing HTML.

The '1 language' version requires you to learn language-specific syntax for creating HTML elements.

How is that more compelling?

Re:Thinking in React Hooks by pgt on Friday December 13, 2019 @02:57AM (#21779885)
(not= syntax semantics)
Re:Thinking in React Hooks by drcode on Thursday December 12, 2019 @11:54AM (#21773565)
FWIW, here is the same example in clojurescript qlkit. It doesn't use "reactive atoms", just the usual state map approach. Of course qlkit's selling point (like for Om) is when global state needs to be synchronized to components, which isn't an issue in this example.

  (ns qlkit-example.core
    (:require [qlkit.core :as ql]))
  
  (defcomponent Counter
    (state {:count 0})
    (render [atts {:keys [count] :as state}]
            [:div [:p "You have clicked the button" count " times."]
                  [:button {:on-click #(ql/update-state! update :count inc)} "Increment"]]))
  
  (ql/mount {:component Counter
             :dom-element (js/document.getElementById "app")})
Re:Thinking in React Hooks by pixelbreaker on Thursday December 12, 2019 @11:04AM (#21772989)
What's your point? This is about Hooks vs Class based components.
Re:Thinking in React Hooks by pgt on Thursday December 12, 2019 @11:11AM (#21773074)
If you need to manage state and want to focus on functions instead of objects (classes), try ClojureScript - especially in combination with [DataScript](https://github.com/tonsky/datascript), an immutable database and Datalog query engine. Clojure/ClojureScript's persistent, immutable memory model makes it much nicer for reasoning about state.

Maybe I'm just a dinosaur, but I can't keep up with all the changes to JavaScript, whereas ClojureScript compiles to ECMAScript 3 and the syntax hasn't changed since its release 12 years ago.

Re:Thinking in React Hooks by zackify on Thursday December 12, 2019 @11:12AM (#21773095)
If I had a dollar for every person who says JS moves too fast
Re:Thinking in React Hooks by dgritsko on Thursday December 12, 2019 @11:11AM (#21773084)
This is a blog post about a new feature in React, not a change to JavaScript.
Re:Thinking in React Hooks by pgt on Thursday December 12, 2019 @11:15AM (#21773130)
You are right and I should clarify that.
Re:Thinking in React Hooks by dhucerbin on Thursday December 12, 2019 @02:29PM (#21775378)
Reagent atom is only useState part of hooks. useEffect is all about syncing effects with state/props changes. VDOM is this one specific, privileged thing so it can use return value of the function. Less common effects must use something different.

I'm not aware of such solution in reagent or rum. Something like re-frame comes to mind, but there, all effects are global.

Re:Thinking in React Hooks by xwdv on Thursday December 12, 2019 @11:13AM (#21773102)
Don’t know about others but React Hooks look way easier than whatever this is.
Re:Thinking in React Hooks by drbojingle on Thursday December 12, 2019 @11:11AM (#21773079)
I think you're missing a ) for counter-component
Re:Thinking in React Hooks by pgt on Thursday December 12, 2019 @11:15AM (#21773141)
<p>Thanks, fixed! :)
Re:Thinking in React Hooks by drbojingle on Thursday December 12, 2019 @11:21AM (#21773203)
No sweat! For what it's worth I'm quite interested in ClojureScript/React and I appreciate the example. It's nice to see how it compares to JS/React