12 ms·
(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 [Reagen
by pgt 7y ago
(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/ http://reagent-project.github.io/) or [Rum](https://github.com/tonsky/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"))
- pixelbreaker 7y agoWhat's your point? This is about Hooks vs Class based components.
- pgt 7y agoIf 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 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.
- drbojingle 7y agoI think you're missing a ) for counter-component
- pgt 7y ago<p>Thanks, fixed! :)
- drbojingle 7y agoNo 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
- xwdv 7y agoDon’t know about others but React Hooks look way easier than whatever this is.
- ng12 7y agoI 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.
- mondaygreens 7y agoYeah that's way more readable and intuitive than anything that came before (that I'm aware of)!
- kgwxd 7y ago1 language vs 2 intertwined languages is compelling enough for me.
- ng12 7y agoDo 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 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.
- ArchReaper 7y agoWhat? 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?
- pgt 7y ago(not= syntax semantics)
- lilactown 7y agoI 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 https://github.com/Lokeh/hx or its WIP successor, https://github.com/Lokeh/helix https://github.com/Lokeh/helix (I am the author of these libraries).
- filoeleven 7y agoI 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.
- lilactown 7y agoPerformance, 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/ https://github.com/Lokeh/thump/
- drcode 7y agoFWIW, 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")})
- dhucerbin 7y agoReagent 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.