Last active
November 7, 2021 17:54
-
-
Save pesterhazy/2a25c82db0519a28e415b40481f84554 to your computer and use it in GitHub Desktop.
ClojureScript: bare React with ES6 classes (extending React.Component, no createClass or reagent)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(ns demo.react-cljs-es6-classes | |
(:require [goog.object :as gobj])) | |
;; Demo of using bare React using ES6 classes (without createClass or reagent) | |
;; | |
;; Equivalent of Javascript/JSX: | |
;; | |
;; class MyComponent extends React.Component { | |
;; constructor(props) { | |
;; super(props); | |
;; this.state = {counter: 0}; | |
;; } | |
;; _inc() { | |
;; this.setState((prevState) => ({count: prevState.count})); | |
;; } | |
;; render() { | |
;; return ( | |
;; <div> | |
;; <div>Counter: {this.state.counter}</div> | |
;; <button onClick=_inc>Click me</button> | |
;; </div> | |
;; ); | |
;; } | |
;; } | |
;; | |
;; | |
;; Uses React 16 | |
;; | |
;; h/t Thomas Heller | |
;; https://gist.github.com/thheller/7f530b34de1c44589f4e0671e1ef7533#file-es6-class-cljs-L18 | |
(enable-console-print!) | |
(defn make-component | |
([display-name m] (make-component display-name nil m)) | |
([display-name construct m] | |
(let [cmp (fn [props context updater] | |
(cljs.core/this-as this | |
(js/React.Component.call this props context updater) | |
(when construct | |
(construct this)) | |
this))] | |
(gobj/extend (.-prototype cmp) js/React.Component.prototype m) | |
(when display-name | |
(set! (.-displayName cmp) display-name) | |
(set! (.-cljs$lang$ctorStr cmp) display-name) | |
(set! (.-cljs$lang$ctorPrWriter cmp) | |
(fn [this writer opt] | |
(cljs.core/-write writer display-name)))) | |
(set! (.-cljs$lang$type cmp) true) | |
(set! (.. cmp -prototype -constructor) cmp)))) | |
(def create-element js/React.createElement) | |
(def my-component | |
(make-component "MyComponent" | |
(fn [this] (set! (.-state this) #js{:counter 0})) | |
#js{:render | |
(fn [] | |
(this-as this | |
(create-element "div" | |
nil | |
#js[(create-element "div" | |
#js{:key 1} | |
#js["Counter is " (-> this .-state .-counter)]) | |
(create-element "button" | |
#js{:key 2 | |
:onClick #(.setState this | |
(fn [old] | |
#js{:counter (-> old .-counter inc)}))} | |
#js["Click me"])])))})) | |
(defn run [] | |
(js/ReactDOM.render (create-element my-component) (js/document.getElementById "app"))) | |
(run) |
Hello there 👋 Could this be used to generate classes like in this example of using Cloudflare Workers?
@martinklepsch probably but that example doesn't use inheritance so the complexity might not be necessary. Would something simpler work?
cljs.user=> (defn Foo [a] #js{:bar (fn [] (prn :bar a))})
#'cljs.user/Foo
cljs.user=> (def foo (Foo. 42))
#'cljs.user/foo
cljs.user=> (.bar foo)
:bar 42
Ah thank you! I didn't realize you could just "fake" class
like this! In the end I also had to access this
which prove a bit more complicated with the plain defn
approach but deftype
is actually working really well here.
(deftype Counter [state env]
Object
(initialize [this] ,,,)
(fetch [this request]
(let [path (.-pathname (js/URL. (.-url request)))]
(js/console.log "this-state" (.-state this)) ; state and env are available here
(case path
"/inc" (js/Response. "/inc called")
"/dec" (js/Response. "/dec called")
(js/Response. "Only /inc and /dec allowed")))))
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I was looking for this everywhere