diff --git a/latest/index.html b/latest/index.html index 0a934d3..9101265 100644 --- a/latest/index.html +++ b/latest/index.html @@ -1,3 +1,3 @@ -funcool/rumext

funcool/rumext

Topics

Namespaces

rumext.v2

\ No newline at end of file +funcool/rumext

funcool/rumext

Topics

Namespaces

rumext.v2

\ No newline at end of file diff --git a/latest/rumext.v2.html b/latest/rumext.v2.html index 166cf8b..1250a56 100644 --- a/latest/rumext.v2.html +++ b/latest/rumext.v2.html @@ -1,6 +1,6 @@ -rumext.v2 documentation

rumext.v2

browser-context?

A boolean var, indicates if the current code is running on browser main thread or not.

+rumext.v2 documentation

rumext.v2

browser-context?

A boolean var, indicates if the current code is running on browser main thread or not.

catch

(catch component {:keys [fallback on-error]})

High order component that adds an error boundary

check-props

(check-props props)(check-props props eqfn?)

Utility function to use with memo'. Will check the props keys to see if they are equal.

Usage:

@@ -10,35 +10,35 @@

)

check-props

macro

(check-props props & [eq-f :as rest])

A macro version of the check-props function

-

Component

The react.Component class

+

Component

The react.Component class

create-context

Create a react context

create-ref

create-root

Creates react root

defc

macro

(defc & args)

A macro for defining component functions. Look the user guide for understand how to use it.

-

defc

macro

(defc & args)

A macro for defining component functions. Look the user guide for understand how to use it.

-

deferred

(deferred component)(deferred component sfn)

A higher-order component that just deffers the first render to the next tick

+

defc

macro

(defc & args)

A macro for defining component functions. Look the user guide for understand how to use it.

+

deferred

(deferred component)(deferred component sfn)

A higher-order component that just deffers the first render to the next tick

deps

(deps)(deps a)(deps a b)(deps a b c)(deps a b c d)(deps a b c d e)(deps a b c d e f)(deps a b c d e f g)(deps a b c d e f g h)(deps a b c d e f g h & rest)

A helper for creating hook deps array, that handles some adaptations for clojure specific data types such that UUID and keywords

deref

(deref iref)

A rumext hook for deref and watch an atom or atom like object. It internally uses the react.useSyncExternalSource API

element

(element klass)(element klass props)

Create a react element. This is a public API for the internal jsx function

fnc

macro

(fnc & args)

A macro for defining inline component functions. Look the user guide for understand how to use it.

-

fnc

macro

(fnc & args)

A macro for defining inline component functions. Look the user guide for understand how to use it.

-

forward-ref

lets your component expose a DOM node to parent component with a ref.

+

fnc

macro

(fnc & args)

A macro for defining inline component functions. Look the user guide for understand how to use it.

+

forward-ref

lets your component expose a DOM node to parent component with a ref.

Fragment

The `react.Fragment class

-

html

macro

(html body)

html

macro

(html body)

hydrate-root

Lets you display React components inside a browser DOM node whose HTML content was previously generated by react-dom/server

+

html

macro

(html body)

html

macro

(html body)

hydrate-root

Lets you display React components inside a browser DOM node whose HTML content was previously generated by react-dom/server

lazy

A helper for creating lazy loading components.

lazy-component

macro

(lazy-component ns-sym)

A macro that helps defining lazy-loading components with the help of shadow-cljs tooling.

-

lazy-component

macro

(lazy-component ns-sym)

A macro that helps defining lazy-loading components with the help of shadow-cljs tooling.

-

memo

(memo component)(memo component eq?)

High order component for memoizing component props. Is a rumext variant of React.memo what accepts a value comparator function (instead of props comparator)

+

lazy-component

macro

(lazy-component ns-sym)

A macro that helps defining lazy-loading components with the help of shadow-cljs tooling.

+

memo

(memo component)(memo component eq?)

High order component for memoizing component props. Is a rumext variant of React.memo what accepts a value comparator function (instead of props comparator)

memo'

A raw variant of React.memo.

merge-props

(merge-props props1 props2)

mount

Add element to the DOM tree. Idempotent. Subsequent mounts will just update element.

portal

Render element in a DOM node that is ouside of current DOM hierarchy.

Profiler

The react.Profiler class

provider

(provider ctx)

Get the current provider for specified context

ref-val

(ref-val ref)

Given state and ref handle, returns React component.

-

render!

(render! root element)

set-ref-val!

(set-ref-val! ref val)

spread-obj

macro

(spread-obj target & [other :as rest])

A helper for create spread js object operations. Leaves the keys untouched.

-

spread-obj

macro

(spread-obj target & [other :as rest])

A helper for create spread js object operations. Leaves the keys untouched.

-

spread-props

macro

(spread-props target & [other :as rest])

A helper for create spread js object operations. Adapts compile time known keys to the react props standard transformations.

-

spread-props

macro

(spread-props target & [other :as rest])

A helper for create spread js object operations. Adapts compile time known keys to the react props standard transformations.

-

start-transition

An alias for react.startTransition function

+

render!

(render! root element)

set-ref-val!

(set-ref-val! ref val)

spread

macro

(spread target & [other :as rest])

A helper for create spread js object operations. Leaves the keys untouched.

+

spread

macro

(spread target & [other :as rest])

A helper for create spread js object operations. Leaves the keys untouched.

+

spread-props

macro

(spread-props target & [other :as rest])

A helper for create spread js object operations. Adapts compile time known keys to the react props standard transformations.

+

spread-props

macro

(spread-props target & [other :as rest])

A helper for create spread js object operations. Adapts compile time known keys to the react props standard transformations.

+

start-transition

An alias for react.startTransition function

Suspense

The react.Suspense class

throttle

(throttle component ms)

A higher-order component that throttles the rendering

undefined

unmount

Removes component from the DOM tree.

@@ -74,9 +74,9 @@

useState

The react.useState hook function

useTransition

The react.useTransition hook function

with-effect

macro

(with-effect deps & body)

A convenience syntactic abstraction (macro) for useEffect

-

with-effect

macro

(with-effect deps & body)

A convenience syntactic abstraction (macro) for useEffect

-

with-layout-effect

macro

(with-layout-effect deps & body)

A convenience syntactic abstraction (macro) for useLayoutEffect

-

with-layout-effect

macro

(with-layout-effect deps & body)

A convenience syntactic abstraction (macro) for useLayoutEffect

-

with-memo

macro

(with-memo deps & body)

A convenience syntactic abstraction (macro) for useMemo

-

with-memo

macro

(with-memo deps & body)

A convenience syntactic abstraction (macro) for useMemo

-
\ No newline at end of file +

with-effect

macro

(with-effect deps & body)

A convenience syntactic abstraction (macro) for useEffect

+

with-layout-effect

macro

(with-layout-effect deps & body)

A convenience syntactic abstraction (macro) for useLayoutEffect

+

with-layout-effect

macro

(with-layout-effect deps & body)

A convenience syntactic abstraction (macro) for useLayoutEffect

+

with-memo

macro

(with-memo deps & body)

A convenience syntactic abstraction (macro) for useMemo

+

with-memo

macro

(with-memo deps & body)

A convenience syntactic abstraction (macro) for useMemo

+
\ No newline at end of file diff --git a/latest/user-guide.html b/latest/user-guide.html index 1b84146..5113c79 100644 --- a/latest/user-guide.html +++ b/latest/user-guide.html @@ -58,50 +58,51 @@

Props & Dest
(mf/defc title
   {::mf/props :obj}
   [{:keys [name] :as props}]
-  (assert (object? props) "expected map")
+  (assert (object? props) "expected object")
   (assert (string? name) "expected string")
   (assert (unchecked-get props "name") "expected string")
 
-  [:div {:class "label"} name])
+  [:label {:class "label"} name])
+
+

An additional idiom, only available in case of using destructuring with props as a js object: is the possibility of obtaining an object with all properties not destructured:

+
(mf/defc title
+  {::mf/props :obj}
+  [{:keys [name] :as props :rest other}]
+  (assert (object? props) "expected object")
+  (assert (object? other) "expected map")
+  (assert (nil? (unchecked-get other "name")) "no name in other")
+
+  ;; The `:>` will be explained later
+  [:> :label other name])
 
-

JSX & Call Conventions

+

This allows you to extract the props that the component has control of and leave the rest in an object that can be passed as is to the next element.

+

JSX / Hiccup

You may be already familiar with hiccup syntax (which is equivalent to the react JSX) for defining the react dom. The intention on this section is explain only the essential part of it and the peculiarities of rumext.

-

Native elements

-

Lets start with simple generic components like :div:

+

Introduction

+

Lets start with simple generic elements like div:

[:div {:class "foobar"
        :style {:background-color "red"}
        :on-click some-on-click-fn}
   "Hello World"]
 
-

As you can observe, looks very familiar. The props and the style are transformed at compile time to a js object transforming all keys from lisp-case to camelCase (and rename :class to className); so the result will look aproximatelly like this in jsx:

-
const h = React.createElement;
+

The props and the style are transformed at compile time to a js object transforming all keys from lisp-case to camelCase (and rename :class to className); so the compilation result to something like this:

+
const h = React.createElement;
 
 h("div", {className: "foobar",
           style: {"backgroundColor": "red"},
           onClick=someFn},
           "Hello World");
 
-

It should be noted that this transformation is only done to properties that are keyword type and that properties that begin with data- and aria- are left as is without transforming just like the string keys.

-

Obviously the keyword properties can be passed directly using camelCase syntax (as react nativelly expects) but for convenience the rumext compiler converts lisp-case to camelCase for you (on-click -> onClick).

-

There are times when we will need the element name to be chosen dynamically or constructed in runtime. For these cases rumext offers handlers. For this specific case the handler is [:> ...].

-

Using the same example as before, the equivalent code would be:

-
[:> "div" {:class "foobar"
-           :style {:background-color "red"}
-           :on-click some-on-click-fn}
-  "Hello World"]
-
-

Since rumext uses compile-time transformations (macros) to transform data structures from clojure to the react dom, all data must be literals that the macro can understand. But there are times when we need to be able to build the props dynamically and in this case we have no choice but to build the props in a javascript object.

-
(let [props #js {:className "fooBar"
-                 :style #js {:backgroundColor "red"}
-                 :onClick some-on-click}]
-  [:> "div" props "Hello World"])
-
-

User defined components

-

Components are everything that we as users define.

-

In this case we have two ways to call our component (or in react words, create the react-dom element from a user-defined component):

-

A: When we have 100% control of the props and we do not want any type of transformation to be done to them (usually when we are talking about large components, you probably do not reuse that they represent a page or a section of that page, but not limited to).

-

B: When we are creating a reusable component that is probably wrapping one or more native elements of the virtual dom and we simply want to extend its behavior controlling only a subset of props, where the rest of the props that are not controlled would be passed as is to the next native element.

-

For the A case, we will use the [:& ...] handler:

+

It should be noted that this transformation is only done to properties that are keyword type and that properties that begin with data- and aria- are left as is without transforming just like the string keys. The properties can be passed directly using camelCase syntax (as react nativelly expects) if you want.

+

Handlers & Call Conventions

+

There are times when we’ll need:

+ +

For this purpose, rumext exposes a special handlers, each one exposing its own call convention: :& and :>.

+

Lets start with :& handler. We will use it when we have 100% control of the props and we do not want any type of transformation to be done to them (usually when we are talking about large components, you probably do not reuse that they represent a page or a section of that page, but not limited to). All props passed to the element will be passed as-is, without any kind of transformations to the prop keys nor values.

(mf/defc title
   {::mf/props :obj}
   [{:keys [name on-click]}]
@@ -111,42 +112,67 @@ 

User def [] [:& title {:name "foobar" :on-click some-fn}])

-

The [:& handler do not perform any case transformation to props. So prop keys and its values will be received as they are supplied on creating the element.

-

For the B case, we will use the already known [:> ...] handler:

-
(mf/defc button
+

And for completeness, an example without destructuring:

+
(mf/defc title
   {::mf/props :obj}
-  [{:keys [name onClick]}]
-  [:button {:on-click onClick} name])
+  [props]
+  (let [name     (unchecked-get props "name")
+        on-click (unchecked-get props "on-click")]
+    [:div {:class "label" :on-click on-click} name]))
 
 (mf/defc my-big-component
   []
-  [:> button {:name "foobar" :on-click some-fn}])
+  [:& title {:name "foobar" :on-click some-fn}])
 
-

In this example, we are creating a react element from user defined button component in the same way as we do it with native DOM elements. Following the same transformation rules (the prop literals passed to the [:> handler will be transformed automatically using react props naming rules, as explained previously).

-

Special case with components ending in * on the name

-

For convenience, if the component is named with an * at the end of the name (or it has the ::mf/props :react in the metadata instead of ::mf/props :obj), the destructuring can use the lisp-case and the macro will automatically access the value with camelCase from the props, respecting the react convention for props.

-

Useful when you build a native element wrapper and the majority of props will be passed as-is to the wrapped element.

-
(mf/defc button*
-  [{:keys [name on-click class]}]
-  [:button {:on-click on-click :class class} name])
+

This is probably the handler that you will use the most time.

+

Then, we also have the :> handler. We will use it when we have the following situations:

+
    +
  • We want to decide the element name dynamicaly
  • +
+
(let [element (if something "div" "span")]
+  [:> element {:class "foobar"
+               :style {:background-color "red"}
+               :on-click some-on-click-fn}
+    "Hello World"])
+
+
    +
  • We want to build and pass props dinamically to create a DOM native element (note that the props are always js plain objects and using react naming convention)
  • +
+
(let [props #js {:className "fooBar"
+                 :style #js {:backgroundColor "red"}
+                 :onClick some-on-click}]
+  [:> "div" props "Hello World"])
+
+
    +
  • we are creating a reusable component that is probably wrapping one or more native elements of the virtual dom and we simply want to extend its behavior controlling only a subset of props, where the rest of the props that are not controlled would be passed as is to the next native element.
  • +
+
(mf/defc my-label
+  {::mf/props :obj}
+  [{:keys [name className] :rest props}]
+  (let [class (or className "my-label")
+        props (mf/spread props {:className class})]
+    [:> :label props name]))
 
-(mf/defc my-big-component
+(mf/defc other-component
   []
-  ;; note: we use here camel case just for demostration purposes and it
-  ;; is not really needded becaue the macro will do it for you
-  [:> button* {:name "foobar" :onClick some-fn :className "foobar"}])
+  [:> my-label {:name "foobar" :on-click some-fn}])
 
-

But remember, the * only changes the behavior of destructuring. The call convention is determined by the used handler: [:& or [:>.

+

So, all the the props passed to the :> handler on creating an element from my-label component are transformed at compile-time to an js object following react convention (camelcasing keys, etc.); This greatly facilitates the task of passing the props to the next element without performing any additional transformation.

+

For this last case and to make the work even easier but preserving the same code style as the rest of the application, rumext offers a way for the destructuring to also take into account the rules and conventions of react for the props keys.

+

This is achieved with the metadata {::mf/props :react} or by putting the suffix * in the component name. And then, the destructuring can use the lisp-case and the macro will automatically access the value with camelCase from the props, respecting the react convention.

+
(mf/defc my-label*
+  {::mf/props :obj}
+  [{:keys [name class] :rest props}]
+  (let [class (or class "my-label")
+        props (mf/spread-props props {:class class})]
+    [:> :label props name]))
+
+

But remember: the * only changes the behavior of destructuring. The call convention is determined by the used handler: [:& or [:>.

Props Checking

Rumext comes with basic props checking that allows basic existence checking or with simple predicate checking. For simple existence checking, just pass a set with prop names.

-
(ns my.ns
-  (:require
-    [rumext.v2 :as mf]
-    [rumext.v2.props :as-alias mf.props]))
-
-(mf/defc button
+
(mf/defc button
   {::mf/props :obj
-   ::mf.props/expect #{:name :on-click}}
+   ::mf/expect #{:name :on-click}}
   [{:keys [name on-click]}]
   [:button {:on-click on-click} name])
 
@@ -154,12 +180,12 @@

Props Checking

You also can add some predicates:

(mf/defc button
   {::mf/props :obj
-   ::mf.props/expect {:name string?
-                      :on-click fn?}}
+   ::mf/expect {:name string?
+                :on-click fn?}}
   [{:keys [name on-click]}]
   [:button {:on-click on-click} name])
 
-

The props checking can be disabled on production builds setting the NODE_ENV enviroment variable to production value.

+

The props checking obey the :elide-asserts compiler option and they are removed on production builds.

Higher-Order Components

This is the way you have to extend/add additional functionality to a function component. Rumext exposes one: