GithubHelp home page GithubHelp logo

roman01la / uix Goto Github PK

View Code? Open in Web Editor NEW
430.0 27.0 50.0 4.63 MB

Idiomatic ClojureScript interface to modern React.js

Home Page: https://roman01la.gitbook.io/uix/

License: Eclipse Public License 2.0

Clojure 36.48% HTML 62.63% JavaScript 0.61% Shell 0.28%
clojure clojurescript hiccup react hooks

uix's Introduction

UIx v1 is not actively maintained anymore, consider using its successor v2 at pitch-io/uix

Idiomatic ClojureScript interface to modern React.js

Discuss at #uix on Clojurians Slack. Bug reports, feature requests and PRs are welcome.

UIx v2 is available at https://github.com/pitch-io/uix

Try it in online REPL

Docs and Guides

API Documentation

If you like what I do, consider supporting my work via donation

CircleCI

Clojars updates are pushed occasionally, depend via Git deps to get the most recent updates.

Clojars Project Clojars Project Clojars Project

{:deps {uix.core {:git/url "https://github.com/roman01la/uix.git"
                  :deps/root "core"
                  :sha "{{replace with commit hash}}"}
        uix.dom {:git/url "https://github.com/roman01la/uix.git"
                 :deps/root "dom"
                 :sha "{{replace with commit hash}}"}
        uix.rn {:git/url "https://github.com/roman01la/uix.git"
                :deps/root "rn"
                :sha "{{replace with commit hash}}"}}}
(require '[uix.core.alpha :as uix])
(require '[uix.dom.alpha :as uix.dom])

(defn button [{:keys [on-click]} text]
  [:button.btn {:on-click on-click}
    text])

(defn app []
  (let [state* (uix/state 0)]
    [:<>
      [button {:on-click #(swap! state* dec)} "-"]
      [:span @state*]
      [button {:on-click #(swap! state* inc)} "+"]]))

(uix.dom/render [app] js/root)

Recipes

Features

Hiccup syntax extension

  • [:div#id.class] or [:#id.class]
  • [:> js/Component attrs & children] - interop with JS components
  • [:<> attrs & children] - React.Fragment
  • [:# {:fallback element} & children] - React.Suspense

Hooks

React Hooks in idiomatic Clojure style

;; state hook
;; (mutable ref type, re-renders component when mutated)
(let [state (uix/state 0)]
  (swap! state inc)
  @state) ; 1

;; ref hook
;; (mutable ref type, doesn't cause re-renders)
(let [ref (uix/ref 0)]
  (swap! ref inc)
  @ref) ; 1

;; effect hook
(uix/effect!
  (fn []
    (prn "after update")
    #(prn "before unmount"))
  [deps])

;; convenience macro for uix.core/effect!
(uix/with-effect [deps]
  (prn "after update")
  #(prn "before unmount"))

;; more in uix.core.alpha ns

Attributes syntax extension

Injects provided function into attributes transformation stage. Could be used for various side effects, such as processing styles with CSS-in-JS libraries (see uix.recipes.dynamic-styles).

(uix.core.alpha/add-transform-fn
  (fn [attrs]
    (my-transform-attrs attrs)))

Hiccup pre-compilation (advanced)

NOTE: UIx interpreter is already super fast (3x faster than Reagent and only 2x slower than vanilla React). Use pre-compilation ONLY if you are hitting performance problems.

Compiles Hiccup into inlined React elements at compile-time and hoists constant elements so they can be shared across components in different namespaces (for reference see @babel/plugin-transform-react-inline-elements and @babel/plugin-transform-react-constant-elements). Hoisting is enabled with :optimize-constants compiler option, which is automatically enabled for :optimizations :advanced.

(uix/html
  [:h1 "Title"])

;; emits this
{
  $$typeof: Symbol.for("react.element"),
  key: null,
  ref: null,
  props: { children: "Title" },
  _owner: null
}

Lazy loading components

Loading React components on-demand as Closure modules. See code splitting guide and how lazy loading is used in React with Suspense: guide.

(uix.core.lazy-loader/require-lazy
  '[uix.components :refer [ui-list]])

[:# {:fallback "Loading..."}
  (when show?
    [ui-list])]

Server-side rendering

UIx can be used for SSR or usual templating in both JVM and JavaScript runtimes

Server-side rendering in JVM

See an example in uix.recipes.server-rendering

(uix.dom/render-to-string element) ;; see https://reactjs.org/docs/react-dom-server.html#rendertostring
(uix.dom/render-to-static-markup element) ;; see https://reactjs.org/docs/react-dom-server.html#rendertostaticmarkup

;; Streaming HTML
(uix.dom/render-to-stream element {:on-chunk f}) ;; see https://reactjs.org/docs/react-dom-server.html#rendertonodestream
(uix.dom/render-to-static-stream element {:on-chunk f}) ;; see https://reactjs.org/docs/react-dom-server.html#rendertostaticnodestream

Server-side rendering in JS

SSR works in JavaScript environment via React's serializer using same API.

  1. Add ReactDOMServer into your dependencies (as cljsjs/react-dom-server or any other way)
  2. Run (uix.dom/render-to-string element)

Benchmarks

  • Hiccup interpretation clojure -A:dev:benchmark:bench-front
  • SSR on JVM clojure -A:dev:benchmark:bench-ssr

Hiccup interpretation

react x 23866 ops/s, elapsed 419ms
uix-interpret x 11848 ops/s, elapsed 844ms
reagent-interpret x 4031 ops/s, elapsed 2481ms

SSR on JVM

lib test 1 test 2 test 3
rum 107.8 µs 3.6 ms 7.7 ms
uix 120.8 µs 3.8 ms 8.1 ms
uix streaming 115.7 µs 3.4 ms 7.6 ms
hiccup 205.7 µs 6.5 ms 16.6 ms

TodoMVC bundle size

lib size gzip
rum 254KB 70KB
reagent 269KB 74KB
uix 234KB 65KB

Figwheel

When developing with Figwheel it is recommended to mark root render function with ^:after-load meta, so Figwheel can update UI tree once the code was re-evaluated.

(ns ^:figwheel-hooks my.ns)

(defn ^:after-load render []
  (uix.dom/render [app] js/root))

React DevTools

When inspecting UI tree in React DevTools, filter out memo components to get cleaner view of components tree.

Testing

scripts/test

Note: to ensure you're using the right Node.js version, you can use nvm and run nvm use once in the directory. Otherwise the Node.js version you use is in the .nvmrc file. See nvm repo for more documentation.

Who’s using UIx

uix's People

Contributors

abcdw avatar caleb avatar codeasone avatar crofty avatar darwin avatar den1k avatar djebbz avatar galdolber avatar jimmyhmiller avatar mhuebert avatar nwjsmith avatar olimsaidov avatar roman01la avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

uix's Issues

Setting the :id key in the prop map does not set the element id

Describe the bug
Element ids can be specified with the :div#a syntax but not with an :id key in the prop map.

Include minimal repro

(uixc/render-to-static-markup [:div#a])                     ;; => "<div id=\"a\"></div>"
(uixc/render-to-static-markup [:div {:id "a"}])             ;; => "<div></div>"

Expected behavior

(uixc/render-to-static-markup [:div {:id "a"}]) ;; => "<div id=\"a\"></div>"

Trying to js/Fetch via POST

Hi, I've been using xframe with great satisfaction and I've run into a jam. Looking through the recipes, I can't seem to find a js/Fetch example via POST to be dispatched by a component.
So I tried writing my own:

(xf/reg-fx
 :http-post
 (fn [_ [_ {:keys [url form-data on-ok on-failed]}]]
   (let [opts {:method "POST"
               :body form-data
              :headers {:Content-Type "application/json"}}
         ]
     (-> (js/fetch url (bean/->js opts))
         (.then #(if (.-ok %)
                   (.json %)
                   (xf/dispatch [on-failed %])))
         (.then bean/->clj)
         (.then #(xf/dispatch [on-ok %]))))))

I think I'm messing up the :body property because method and Content-Type work, but on the server side I get [object Object] and my ring app replies: Malformed JSON in request body.. Do I need to do some preprocessing ? form-data is a clojure map.

Recipes broken?

In dev/uix/recipes.cljs the command clojure -A:dev -m uix.recipes.server-rendering produces a an HTML broken. It consists just of the "Lorem ipsum" text repeated all over the page. I see no select to choose a recipe.

SSR clojurescript

Wanted to say first that I love what you are doing with uix.

I was curious if there was any reason you left out SSR for a clojurescript application. You have the functions defined for clj, but not cljs.

Given what you have in the compiler it seems like you could just use compiler/as-element and the methods in react-dom/server to implement those.

Any opposition to that or just haven't done it yet? Thanks again for the great library.

SSR omits true values for some attributes

Describe the bug
SSR omits true values for attributes like content-editable. Upon hydrate these appear on the frontend, resulting in a warning:
image

Include minimal repro
in CLJ

(uix.dom/render-to-string [:div {:content-editable true}])
=> "<div contenteditable=\"\"></div>"

in CLJS (using react.dom.server)

(uix.dom/render-to-string [:div {:content-editable true}])
=> "<div contenteditable=\"true\" data-reactroot=\"\"></div>"

Expected behavior
CLJ output that does not throw warnings in hydrate

Explore alternative approaches of UIX components implementation in React

My issue is that currently each UIX component creates two backing react components. React.memo which wraps a function component derived from UIX component (defn). I understand that memo wrapping is needed to implement component update optimization using clojure semantic for equality.

But this has several drawbacks:

  1. component tree depth is effectively doubled, e.g. in React Devtools
  2. memo wrapping is not always needed and can add unecessary overhead, e.g. when having a component with constant props (see[1])

I'm no React expert. Just looked briefly at the implementation. I think there are two possible avenues to explore:

  1. use old-school React class components with shouldComponentUpdate - I know that function components are more modern and fit better to cljs, but for above reasons I would not rule this out
  2. keep an eye on ReactFundamentalComponent type[2]. There is no specs/docs right now. But from the code I believe this might be preferrable way how to implement uix-like library-driven React components in the future.

In current implementation I would also consider:

  1. make memo wrapping optional - e.g. it could be disabled for dev work via a config, or it could be disabled on component-by-component basis using metadata

No hurry. Just ideas. I'm happy to learn about design decisions behind current implementaion. It is very likely that I don't see all reasons.

[1] https://kentcdodds.com/blog/usememo-and-usecallback
[2] facebook/react#16049

Slack Channel

How would you feel opening a slack channel on Clojurians?

Should uix.core.alpha/memoize only be included in Clojurescript

Right now uix.core.alpha/memoize is compiled in the the Clojure as well as Clojurescript environments:

(defn memoize
"Takes component `f` and comparator function `should-update?`
that takes previous and next props of the component.
Returns memoized `f`.
When `should-update?` is not provided uses default comparator
that compares props with clojure.core/="
([f]
(memoize f default-compare-args))
([f should-update?]
(react/memo f should-update?)))

I'm getting an error when trying to use macros from uix.core.alpha: "RuntimeException No such namespace: react"

Is there something I'm missing, or is this a bug?

-Caleb

Follow up on #45

Describe the bug
Related to #45
While browsers without devtools installed don't throw an error anymore, I noticed that in builds with :optimizations :none use of <sub macro does not result in re-renders even though state is updated. It does work in :optimizations :advanced.

One difference between :none and :advanced optimizations the value of goog.DEBUG which <sub checks for here:

~(if (uix.lib/cljs-env? &env)
`(if ~(with-meta 'goog.DEBUG {:tag 'boolean})
(if (and ~'(exists? js/__REACT_DEVTOOLS_GLOBAL_HOOK__)
(-> (.. ~'js/__REACT_DEVTOOLS_GLOBAL_HOOK__ -renderers) (.get 1) .getCurrentFiber))
~ret
(~get-state-sym))

Following from that goog.DEBUG being true and absence of devtools resuts in the return of (~get-state-sym) which does not cause a re-render.

While this may be the crux of the bug, I also fail to understand how existence of both goog.DEBUG and devtools play into the <sub macro? Shouldn't all cases in cljs return the subscription and all clj cases return the state regardless of other variables?

Include minimal repro
Try this recipe with no compiler optimizations and react devtools not installed (incognito tab).

Expected behavior
<sub to return subscriptions clojurescript and state in clojure.

Useless npm dependencies caching for Circe CI ?

I'm new to the project, but since uix doesn't need npm at all and doesn't even have a package.json file, some of the stuff in .circleci/config.yml are useless. Want a PR ? (I'm in a PR frenzy today)

iAtom implementations of adapton don't return new value

Describe the bug
Calls to either clojure.core/reset! or clojure.core/swap! are supposed to return the updated value of the atom-like object. It looks like the implementations in xframe do not:

https://github.com/roman01la/uix/blob/master/core/src/xframe/core/adapton.clj#L63-L75

Include minimal repro

(reset! (atom {}) {:foo :bar})
=> {:foo :bar}

(reset! (xframe.core.adapton/aref {}) {:foo :bar})
; => nil <-------------- wrong

Expected behavior
Same return value as with clojure.core/atom

(I've only tested this in CLJ.)

Basic global state subscriptions + updates

Is your feature request related to a problem? Please describe.
I'm currently using the re-frame-like xframe for global subscriptions and state updates. However, in many of my use-cases I merely need get-like and get-in-like subscriptions and assoc/update(-in)-like handlers.

Describe the solution you'd like
Reagent's ratom + cursor compatible with reacts hooks. Like this but global.

Describe alternatives you've considered
xframe has been my alternative so far. I think it would be nice to provide a less opinionated solution to global state updates and subscriptions.

Consider useReducer as impl for state

swap! on a (uix/state initial) has different semantics to a Clojure atom. two subsequent calls to swap! use the same value, and effectively acts as a reset.

Any chance this could be moved to be backed by useReducer?

question :re error-boundary

Thanks @roman01la for adding create-error-boundary.

I've tried using it and I'm getting confused since there doesn't seem to be a way to recover from an error-state.

For example:

(defn child []
  [:h1 {:on-mouse-move #(when (> 0.5 (rand))
                          (throw (js/Error. "Error")))}
   "Everything is fine."])

(def error-boundary
  (core/create-error-boundary
    {:error->state (fn [error] {:error error})
     :handle-catch (fn [error info] (println error))}
    (fn [{:keys [error]} [child]]
      (if error
        error
        child))))

;; somewhere in the app
[error-boundary [child]]

How can error-boundary ever recover? How can the error-state be removed in the case that new props are available for child that might not lead to an error?

Bug in global state recipe

state* in https://github.com/roman01la/uix/blob/master/core/dev/uix/recipes/global_state.cljc#L9 never changes, instead it remains the first value.

This means the new value nf is always compared to the initial value. I hack-fixed this by ignoring the hook state completely and running f on the old and new state as provided by add-watch, like so

(defn <sub [f]
  #?(:clj
     nil
     :cljs
     (let [state* (uix/state #(f @db))] ;; <- stale
       (uix/effect!
        (fn []
          (let [id            (random-uuid)
                unsub?        (atom false)
                check-updates (fn [o n]
                                (let [of (f o)
                                      nf (f n)]
                                  (when (and (false? ^boolean @unsub?) (not= nf of))
                                    (-reset! state* nf))))]
            (add-watch db id #(check-updates %3 %4))
            (check-updates nil @db)
            #(do
               (reset! unsub? true)
               (remove-watch db id))))
        [f])
       @state*)))

Passing state hook to child components

Firstly, thanks very much for the library, I've enjoyed using it a lot 👍

Describe the bug
Since af41831 I can no longer pass a state hook to a child component. Previously I had been passing some hooks into child components and the child could manipulate the state.

Include minimal repro

(defn app []
  (let [value* (uix/state "")]
    [:div
     [:h1 "UIX issue"]
     [:div
      [:label "Input 1"]
      [:input
       {:type :text
        :value @value*
        :on-change #(reset! value* (.. % -target -value))}]]
     [:div
      [:label "Input 2"]
      [input value*]]
     [:p
      "Using commit 40e9adf74cf21163b15e5c57d1325675809bac19, these inputs work in the same way, typing in either will update the state stored in value*"]
     [:p
      "From commit af4183104938f3c51775b7014435a2f58c7d438d onwards, the inputs behave differently."]]))

Expected behavior
I would expect to be able to pass state hooks to children rather then needing to pass down the deferenced value and a callback.

require-lazy macro may be required with clj/cljs agnostic code

I stumbled upon this:

(:require #?(:cljs [uix.core.alpha :as uix :refer-macros [require-lazy]])
#?(:clj [uix.core.alpha :as uix :refer [require-lazy]])))

Assuming require-lazy is a macro, I believe [uix.core.alpha :as uix :refer [require-lazy]] should work in both cases as long as cljs file for uix.core.alpha does :require-macros [uix.core.alpha]. I'm not sure about uix.core.alpha implemented as cljc file, but if it was .clj and .cljs it would definitely work.

See "implicit refer" in this blog post: https://blog.fikesfarm.com/posts/2016-03-01-clojurescript-macro-sugar.html

Multiple db's in xframe

Is your feature request related to a problem? Please describe.
xframe.core.alpha defines 1 db and 1 subs-registry which means if one wanted to render multiple apps using xframe side by side they'd have to share one db.

https://github.com/roman01la/uix/blob/master/core/src/xframe/core/alpha.cljc#L31

Describe the solution you'd like
Support for multiple db's in xframe

Describe alternatives you've considered
Multiple apps sharing one db by using a keyword as a lookup namespace for an app. This is possible but feels hacky since separate apps should truly be separate.

Move files in resources/public to alias in deps

Currently, resources/public is included in the top-level :paths in deps.edn. This can cause conflicts with library consumers as it is very common to serve static assets from the classpath. For example, figwheel-main looks for public/index.html on the classpath and picks up the file from uix if included in deps.

Since these files seem to be for development and benchmarking only, I suggest moving them into another resources folder and adding them to the classpath via :extra-paths in an alias.

Thanks for the library!

Trick for hot-reloading components

Since UIX switched to React.memo wrapping the components, hot reloading of components where props are the same stopped working. Here's a trick for getting that back in development. Didn't know where to leave this so an issue it is :)

(when ^boolean js/goog.DEBUG
  (set! react/memo (fn [& [f]] f)))

Cursive: cycle dependency in cljc

Cursive complains about cycle dependency when trying to evaluate (in REPL, both JVM and JS) cljc namespace that has :require-macros on itself within cljs reader conditional?
D1NqbaRWsAA7U9D

Steps to reproduce

CLJS REPL is optional, the issue is still reproducible in Clojure JVM REPL

  1. Setup (as shown below) and run both Clojure JVM & ClojureScript REPLs

Run:Debug Configurations 2019-03-10 12-35-28

Run:Debug Configurations 2019-03-10 12-35-54

  1. Start Figwheel REPL from CLJS REPL by executing (start)
  2. Switch to uix.core ns and try to load it in Clojure JVM REPL

Feature request: optional user-specified `format-display-name`

I'd like to have a way how to set custom format-display-name (in my dev builds).

My namespaces are quite long and this would be a centralized way how to strip common prefix from component names. Also one could apply app-specific knowlege on forming component names.

Context doesn't work on JVM with Hiccup syntax

I asked about this on Slack but I'm also opening an issue as it may help others and it allows me to track it.

Describe the bug
Context doesn't work on the JVM when using Hiccup syntax for calling components.

Include minimal repro

(uix/defcontext *ctx*)

(defn foo []
  [:div (uix/context *ctx*)] )

(defn bar []
  (uix/context-provider [*ctx* 1]
    [foo]))

(uixc/render-to-string [bar])
;; => "<div>Unbound: *ctx*</div>"

Expected behavior

"<div>1</div>"

Changing [foo] from the Hiccup syntax to a regular function call (foo) sidesteps the issue but means Hiccup syntax can't be used.

Portal Demo should use LayoutEffect for getting dom node

Describe the bug
Portal Demo should use LayoutEffect for getting dom node

Include minimal repro
currently, demo query the dom node in rendering function which break the purity of rendering function.

I try to follow this example
https://blog.logrocket.com/build-modal-with-react-portals/

end up having this code

(let [dom (uix/state nil)]
    (uix/layout-effect!
     (fn []
       (let [ele (.getElementById js/document wrapper-id)]
         (when ele
           (reset! dom ele))))
     [wrapper-id])
    (when-let [node @dom]
      (dom/create-portal child node)))

Expected behavior
Demo should show a common practice way to use the API, since people learn by example.

Additional context
..

Help!

Describe the bug

Hi Roman!

I'm moving my component library root from reagent to UIX to allow for SSR. In vain I've spent all of yesterday trying to get make my example app to be reactive as it is in reagent.

For some background, root is a recursive data-dispatch-based component rendering library. Given a lookup function and a root-id it will resolve a datom to its component and it with equally resolved child-components. This enables it to create reactive views on data in any form. To change the arrangement of the components one only needs to mutate the data returned from lookup, i.e. no need to sprinkle conditionals all over component code. For example, root could work very well to implement a AST based code editor or to implement a unstructured document editor similar to notion.so.

At one point I've had it set up with xframe dispatches and subscriptions, at another with <sub that you helped me implement months ago in my project Zeal. While it works with reagent, all attempts trying to make it work with UIX resulted in Uncaught Error: Rendered more hooks than during the previous render...

At this point I'm out of ideas ¯_(ツ)_/¯ I'd appreciate any help! Root will be open sourced soon and I hope it will be a useful and powerful library for the Clojure community. Before doing so I just want to make sure I have a somewhat robust API and real project (which I'm working on) to go along with it.

Include minimal repro
I added you as a contributor.
https://github.com/den1k/root/blob/master/examples/debug/core.cljs
$ yarn watch
http://localhost:8700/

Comment reagent out for uix:
https://github.com/den1k/root/blob/master/examples/debug/core.cljs#L39-L40

Expected behavior
With reagent clicking the button updates the the component. Using uix with a simple lookup nothing happens. Using uix with <sub or xf/dispatch + xf/sub breaks the app while throwing Uncaught Error: Rendered more hooks than during the previous render.

🙏️

Portal API enhancement suggestion

Me again :)

The current API (at least in the popup recipe) suggests using either a keyword for the CSS selector or a DOM node. The problem I see with CSS as a Clojure keyword is that they give only a tiny subset of what's possible as opposed to normal CSS selectors (as strings). Ex: How do you do "aside .item:first-child" with a keyword? Should it accept CSS selectors as strings instead of keywords?

Prefix sha not supported, use full sha for uix.core/uix.core

Describe the bug
Quick starting the project, running lein-tools-deps "0.4.5" and adding deps.edn as suggested, I get clojure.lang.ExceptionInfo: Prefix sha not supported, use full sha for uix.core/uix.core

Include minimal repro
In my project.clj I added:

  :plugins [[lein-tools-deps "0.4.5"]]
  :middleware [lein-tools-deps.plugin/resolve-dependencies-with-deps-edn]
  :lein-tools-deps/config {:config-files [:install :user :project]}

and in deps.clj :

{:deps {uix.core {:git/url "https://github.com/roman01la/uix.git"
                  :deps/root "core"
                  :sha "{{commit hash}}"}
        uix.dom {:git/url "https://github.com/roman01la/uix.git"
                 :deps/root "dom"
                 :sha "{{commit hash}}"}
        uix.rn {:git/url "https://github.com/roman01la/uix.git"
                :deps/root "rn"
                :sha "{{commit hash}}"}}}

Expected behavior
It should pull the project from this git repo. Instead I get an error.

Additional context
Will look more into this.

xframe <sub __REACT_DEVTOOLS_GLOBAL_HOOK__ is not defined

Describe the bug
I'm getting the error: ReferenceError: __REACT_DEVTOOLS_GLOBAL_HOOK__ is not defined in the devtools console.

I'm using the latest commit 6e06ec2

I think the error is coming from https://github.com/roman01la/uix/blob/master/core/src/xframe/core/alpha.cljc#L127

Include minimal repro
Evaluating js/__REACT_DEVTOOLS_GLOBAL_HOOK__ in the cljs repl throws the same error:

#object[ReferenceError ReferenceError: __REACT_DEVTOOLS_GLOBAL_HOOK__ is not defined]
	 (<NO_SOURCE_FILE>)

Using (exists? js/__REACT_DEVTOOLS_GLOBAL_HOOK__) returns false. I notice that the cljs files use the exists? check before referring to __REACT_DEVTOOLS_GLOBAL_HOOK__.

I tried modifying the <sub macro to use exists, but I kept tripping up on the macro syntax.

Unable to render id when applied as part of attribute map

Describe the bug
There seems to be a difference in the way that elements are rendered between clj and cljs. In clj when an id is applied to an element as part of an attribute map that id is not rendered out. In cljs the id does get rendered as expected.

If the id is applied in css selector type format eg div#id the the id does get rendered out in both clj and cljs.

Include minimal repro

  1. Example 1 - id applied with # format to element tagname.
    In a clj (or cljc) file add the input line and send it to your clj REPL to see the element rendered as a string with an id attribute.
    Input:
    (uix.dom.alpha/render-to-string [:div#id "hi"])
    Output:
    "<div id=\"id\">hi</div>"
    ✅ Correct output

  2. Example 2
    Add the following input line to a clj (or cljc) file and send to REPL
    Input:
    (uix.dom.alpha/render-to-string [:div {:id "id"} "hi"])
    Output:
    "<div>hi</div>"
    👎 Unexpected output

Expected behavior
I was expecting the same output for both examples, so that the id applied to an attribute map for an element would get rendered and not ignored.

Additional context
Nope!

Thanks 👍

Make dependencies for uix/memo and uix/callback Clojurescript friendly

Right now uix/memo and uix/callback just directly call React's underlying useMemo and useCallback which will trigger a re-calculation of the value/callback on ever render when used with Clojurescript datastructures.

What do you think about re-working these to use the same helpers as uix/effect! and uix/layout-effect!.

Here is something that I found works in my project to prevent unnecessary re-calculations:

(defn callback
  "Takes function f and optional vector of dependencies, and returns f."
  ([f]
   (callback f nil))
  ([f deps]
    #?(:cljs (with-deps-check
               [prev-deps*]
               (react/useCallback (fn [& args]
                                    (reset! prev-deps* deps)
                                    (apply f args))
                                  (maybe-js-deps @prev-deps*))
               deps)
       :clj  f)))

(defn memo
  "Takes function f and optional vector of dependencies, and returns memoized f."
  ([f]
   (memo f nil))
  ([f deps]
    #?(:cljs (with-deps-check
               [prev-deps*]
               (react/useMemo (fn []
                                (reset! prev-deps* deps)
                                (f))
                              (maybe-js-deps @prev-deps*))
               deps)
       :clj  (f))))

(The above version of memo also calls f in the clj branch, since useMemo returns the value from the function, not the function itself, right now uix/memo returns the function itself in memo when run in Clojure)

Can't run Front-end benchmark

The README command clojure -A:dev:benchmark -m figwheel.main -O advanced -bo benchmark just produces a javascript. I haven't find a way to actually run the front benchmark. I tried manually with clojure -A:dev:benchmark -m cljs.main -re node -co benchmark.cljs.edn -m uix.benchmark without success. How do you do ?

Benchmarks against Rum on the JVM

Hello @roman01la ! Long time no Github issues :)

First of all, I've just discovered your library, which might be what we need to to move from Rum/Citrus to modern and more efficient React wrapper. I need to evaluate it more, and if it fits the bill, expect contributions from me as usual :)

I was just wondering that it could be nice to have a benchmark against Rum on the JVM itself.

Have a nice day !

Splitting library into multiple packages

I may use UIx on React Native project soon, but the library is not compatible with multiple render targets at the moment because it directly depends on react-dom. The plan is to split UIx into uix.core, uix.dom and uix.native libraries all living in a monorepo here.

Bundle size?

I'm very curious about the final Javascript bundle size. Did you compare a simple app written with Reagent and/or Rum vs UIx? I'd love to know the difference.

lazy_loader computes js module name from cljs namespace in an overly restrictive way

lazy_loader/require-lazy is opinionated about how to derive js module name from cljs namespace.
This opinion is inconvenient to my namespace structure

module (->> (str lib)
                             (re-find #"\.([a-z0-9-]+)")
                             second
                             keyword)]

Describe the solution you'd like
I would be happy if I could pass the js module name in to the require-lazy macro,
but I understand that Roman has a more far-reaching solution in mind

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.