Karet is a library that allows you to embed Kefir observables into React Virtual DOM. Embedding observables into VDOM has the following benefits:
- It allows you to use
only
functional components,
because you can then use observables for managing state
and
ref
for component lifetime, leading to more concise code. - It helps you to use React in an algorithmically efficient way:
- The body of a functional component is evaluated only once each time the
component is mounted.
- This also helps you to avoid issues such as the gotcha with ref.
- Only elements that contain embedded observables are rerendered when changes are pushed through observables. An update to a deeply nested VDOM element can be an O(1) operation.
- The body of a functional component is evaluated only once each time the
component is mounted.
Using Karet couldn't be simpler. You just import * as React from "karet"
and
you are good to go.
To use Karet, you simply import it as React
:
import * as React from "karet"
and you can then write React components:
const oncePerSecond = Kefir.interval(1000).toProperty(() => {})
const Clock = () =>
<div>
The time is {oncePerSecond.map(() => new Date().toString())}.
</div>
with VDOM that can have embedded Kefir
observables. This works because Karet exports an enhanced version of
createElement
.
NOTE: Karet does not pass through other named React exports. Only
createElement
is exported, which is all that is needed for basic use of VDOM
or the Babel JSX transform.
NOTE: The result, like the Clock
above, is just a React component. If
you export it, you can use it just like any other React component and even in
modules that do not import karet
.
Karet only lifts built-in HTML elements implicitly. The karet-lift
attribute
on a non-primitive element instructs Karet to lift the element.
For example, you could write:
import * as RR from "react-router"
import * as React from "karet"
const Link1 = ({...props}) => <RR.Link karet-lift {...props}/>
to be able to use Link1
with
embedded Kefir observables:
<Link1 href="https://www.youtube.com/watch?v=Rbm6GXllBiw"
ref={elem => elem && elem.focus()}>
{Kefir.sequentially(1000, [3, 2, 1, "Boom!"])}
</Link1>
Note that the ref
attribute is only there as an example to contrast
with $$ref
.
fromKefir
allows one to convert a Kefir observable of React elements into a
React element. It is useful in case the top-most element of a component depends
on a Kefir observable.
For example:
import {fromKefir} from "karet"
import {ifte} from "karet.util"
const Chosen = ({choice}) =>
fromKefir(ifte(choice, <True/>, <False/>))
Here ifte
from karet-util
returns an observable that is <True/>
when
choice
is true and otherwise <False/>
.
Note that the point of using fromKefir
in the above example is that we don't
want to wrap the ifte(...)
inside an additional element like this:
const Chosen = ({choice}) =>
<div>
{ifte(choice, <True/>, <False/>)}
</div>
fromClass
allows one to lift a React component.
For example:
import * as RR from "react-router"
import {fromClass} from "karet"
const Link2 = fromClass(RR.Link)
WARNING: A difficulty with lifting components is that you will then need to
use the $$ref
attribute, which is not necessary when
using karet-lift
to lift an element.
The $$ref
attribute on an element whose component is lifted using fromClass
<Link2 href="https://www.youtube.com/watch?v=Rbm6GXllBiw"
$$ref={elem => elem && elem.focus()}>
{Kefir.sequentially(1000, [3, 2, 1, "Boom!"])}
</Link2>
does the same thing as the ordinary
JSX
ref
attribute:
JSX/React treats ref
as a special case and it is not passed to components, so
a special name had to be introduced for it.