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).
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.
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.
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.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?
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.The '1 language' version requires you to learn language-specific syntax for creating HTML elements.
How is that more compelling?
(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")})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.
I'm not aware of such solution in reagent or rum. Something like re-frame comes to mind, but there, all effects are global.
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:
The same example using Rum: