Karet is a library that allows you to embed Kefir properties into React Virtual DOM. Embedding observable properties into VDOM has the following benefits:
- It allows you to use only functional components, because you can then use observables for managing state and 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.
- Only elements that contain embedded properties are rerendered when changes
are pushed through observables. An update to a deeply nested VDOM element
can be an
O(1)
operation.
Using Karet couldn't be simpler. Usually 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 App = () => (
<div>
<h1>What is the date and time</h1>
{Kefir.interval(1000)
.toProperty(() => {})
.map(() => new Date().toString())}
</div>
)
with VDOM that can have embedded Kefir
properties. This works because Karet exports an enhanced version of
createElement
.
Note that the result, like the date and time display 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
.
Here is a live example in CodeSandbox.
More links to live examples in the Calmm documentation Wiki.
Karet passes through the following exports from React:
Children
as is. Note that with observable properties in children these functions may not do exactly what you want and you might want to lift them.Fragment
as is. It should work without problems.createContext
as is. Note that with Karet it is preferable to put observable properties into the context and let changes propagate through them rather than update the context. Also note that neither the provider nor the consumer are lifted by default. Lifting the consumer will likely cause no issues, but lifting the provider would eliminate observables from thevalue
property and could cause problems. If you need to have observable children inside the provider, you can wrap the children inside aFragment
. See the CodeSandbox examples- Exam Events Form where the context is used to transmit the language as an observable atom, and
- Form using Context where context is used to transmit form properties to form elements.
createElement
which lifts Kefir properties in fragments and built-in HTML elements.forwardRef
as is.useContext
as is.
Notably the following are not exported:
Component
andPureComponent
, because with Karet you really don't need them and therender
method can cause undesired component remounting when used with observable properties embedded into VDOM.cloneElement
does not work out of the box with elements containing Kefir properties. It should be possible to support it, however.createRef
is not exported, because Karet Util provides an alternative that works better with observable properties.Suspense
andlazy
are not needed sinceimport()
returns a promise and you can just convert that to an observable property like this.- Most React hooks are not exported because they are not typically needed in Karet components. State should be handled in atoms. Effects can already be handled using observable properties as children or props.
Karet only lifts built-in HTML elements and
fragments implicitly. The
karet-lift
attribute on a non-primitive element instructs Karet to lift the
element.
For example, you could write:
import Select from 'react-select'
import * as React from 'karet'
// ...
const ReactSelect1 = ({value}) => (
<Select
karet-lift
name="form-field-name"
value={value}
options={options}
onChange={o => value.set(o && o.value)}
/>
)
to be able to use Select
from
React Select with embedded
Kefir Atoms.
Here is a live example in CodeSandbox.
Karet only lifts built-in HTML elements and
fragments implicitly. fromClass
allows one to create lifted version of a given React component.
For example, you could write:
import Select from 'react-select'
import * as React from 'karet'
const SelectLifted = React.fromClass(Select)
const ReactSelect2 = ({value}) => (
<SelectLifted
name="form-field-name"
value={value}
options={options}
onChange={o => value.set(o && o.value)}
/>
)
to be able to use Select
from
React Select with embedded
Kefir Atoms.
Here is a live example in CodeSandbox.
The
React inline elements transform
is incompatible with Karet, because it bypasses React.createElement
. OTOH, the
React constant elements transform
works just fine with Karet.