More docs for dataverse

This commit is contained in:
Aria Minaei 2023-08-10 13:31:54 +02:00
parent 327b859ed4
commit c8430050a8
15 changed files with 2923 additions and 67 deletions

View file

@ -2,3 +2,10 @@
. "$(dirname "$0")/_/husky.sh" . "$(dirname "$0")/_/husky.sh"
yarn lint-staged yarn lint-staged
yarn workspace @theatre/dataverse run precommit
# if there are unstaged changes in ./packages/dataverse/docs, fail
if ! git diff --quiet --exit-code -- docs; then
echo "Please run 'yarn workspace @theatre/dataverse run doc' and commit the changes to the docs folder"
exit 1
fi

View file

@ -1,9 +1,715 @@
# @theatre/dataverse # @theatre/dataverse
Dataverse is the reactive dataflow library [Theatre.js](https://www.theatrejs.com) is built on. It is inspired by ideas in [functional reactive programming](https://en.wikipedia.org/wiki/Functional_reactive_programming) and it is optimised for interactivity and animation. Dataverse is the reactive dataflow library
[Theatre.js](https://www.theatrejs.com) is built on. It is inspired by ideas in
[functional reactive programming](https://en.wikipedia.org/wiki/Functional_reactive_programming)
and it is optimised for interactivity and animation.
Dataverse is currently an internal library. It is used within Theatre.js, but its API is not exposed through Theatre. This is so that we can iterate on the reactive internals while keeping the public API stable. We plan to eventually have an LTS release, but for now, expect a high release cadence and API churn in dataverse while the API in Theatre.js remains stable. Dataverse is currently an internal library. It is used within Theatre.js, but
its API is not exposed through Theatre. This is so that we can iterate on the
reactive internals while keeping the public API stable. We plan to eventually
have an LTS release, but for now, expect a high release cadence and API churn in
dataverse while the API in Theatre.js remains stable.
## Documentation ## Installation
* [The exhaustive guide to dataverse](./src/dataverse.test.ts) ```sh
$ npm install @theatre/dataverse
# and the react bindings
$ npm install @theatre/react
```
## Usage with React
```tsx
import {Atom} from '@theatre/dataverse'
import {useVal} from '@theatre/react'
import {useEffect} from 'react'
// Atoms hold state
const atom = new Atom({count: 0, ready: false})
const increaseCount = () =>
atom.setByPointer(atom.pointer.count, (count) => count + 1)
function Component() {
// useVal is a hook that subscribes to changes in a specific path inside the atom
const ready = useVal(
// atom.pointer is a type-safe way to refer to a path inside the atom
atom.pointer.ready,
)
if (!ready) {
return <div>Loading...</div>
} else {
return <button onClick={increaseCount}>Increase count</button>
}
}
```
Alternatively, we could have defined our atom inside the component, making its
state local to that component instance:
```tsx
import {useAtom} form '@theatre/react'
function Component() {
const atom = useAtom({count: 0, ready: false})
const ready = useVal(atom.pointer.ready)
// ...
}
```
## Quick tour
There 4 main concepts in dataverse:
- [Atoms](#atoms), hold the state of your application.
- [Pointers](#pointers) are a type-safe way to refer to specific properties of
atoms.
- [Prisms](#prisms) are functions that derive a value from an atom or from
another prism.
- [Tickers](#tickers) are a way to schedule and synchronise computations.
### Atoms
Atoms are state holders. They can be used to manage either component state or
the global state of your application.
```ts
import {Atom} from '@theatre/dataverse'
const atom = new Atom({intensity: 1, position: {x: 0, y: 0}})
```
#### Changing the state of an atom
```ts
// replace the whole stae
atom.set({intensity: 1, position: {x: 0, y: 0}})
// or using an update function
atom.reduce((state) => ({...state, intensity: state.intensity + 1}))
// or much easier, using a pointer
atom.setByPointer(atom.pointer.intensity, 3)
atom.reduceByPointer(atom.pointer.intensity, (intensity) => intensity + 1)
```
#### Reading the state of an atom _None-reactively_
```ts
// get the whole state
atom.get() // {intensity: 4, position: {x: 0, y: 0}}
// or get a specific property using a pointer
atom.getByPointer(atom.pointer.intensity) // 4
```
#### Reading the state of an atom _reactively, in React_
```ts
import {useVal} from '@theatre/react'
function Component() {
const intensity = useVal(atom.pointer.intensity) // 4
// ...
}
```
Atoms can also be subscribed to outside of React. We'll cover that in a bit when
we talk about [prisms](#prisms).
### Pointers
Pointers are a type-safe way to refer to specific properties of atoms.
```ts
import {Atom} from '@theatre/dataverse'
const atom = new Atom({intensity: 1, position: {x: 0, y: 0}})
atom.setByPointer(atom.pointer.intensity, 3) // will set the intensity to 3
// referring to a non-existing property is a typescript error, but it'll work at runtime
atom.setByPointer(atom.pointer.nonExistingProperty, 3)
atom.get() // {intensity: 3, position: {x: 0, y: 0}, nonExistingProperty: 3}
```
Pointers are referrentially stable
```ts
assert.equal(atom.pointer.intensity, atom.pointer.intensity)
```
#### Pointers and React
Pointers are a great way to pass data down the component tree while keeping
re-renders only to the components that actually need to re-render.
```tsx
import {useVal, useAtom} from '@theatre/react'
import type {Pointer} from '@theatre/dataverse'
function ParentComponent() {
const atom = useAtom({
light: {intensity: 1, position: {x: 0, y: 0}},
ready: true,
})
const ready = useVal(atom.pointer.ready)
if (!ready) return <div>loading...</div>
return (
<>
{/* <Group> will only re-render when the position of the light changes */}
<Group positionP={atom.pointer.light.position}>
{/* <Light> will only re-render when the intensity of the light changes */}
<Light intensityP={atom.pointer.intensity} />
</Group>
</>
)
}
function Group({positionP, children}) {
const {x, y} = useVal(positionP)
return <div style={{position: `${x}px ${y}px`}}>{children}</div>
}
function Light({intensityP}) {
const intensity = useVal(intensityP)
return <div style={{opacity: intensity}} className="light" />
}
```
### Prisms
Prisms are functions that derive a value from an atom or from another prism.
```ts
import {Atom, prism, val} from '@theatre/dataverse'
const atom = new Atom({a: 1, b: 2, foo: 10})
// the value of this prism will always be equal to the sum of `a` and `b`
const sum = prism(() => {
const a = val(atom.pointer.a)
const b = val(atom.pointer.b)
return a + b
})
```
Prisms can also refer to other prisms.
```ts
const double = prism(() => {
return 2 * val(sum)
})
console.log(val(double)) // 6
```
#### Reading the value of a prism, _None-reactively_
```ts
console.log(val(prism)) // 3
atom.setByPointer(atom.pointer.a, 2)
console.log(val(prism)) // 4
```
#### Reading the value of a prism, _reactively, in React_
Just like atoms, prisms can be subscribed to via `useVal()`
```tsx
function Component() {
return (
<div>
{useVal(atom.pointer.a)} + {useVal(atom.pointer.b)} = {useVal(prism)}
</div>
)
}
```
#### Reading the value of a prism, _reactively, outside of React_
Prisms can also be subscribed to, outside of React's renderloop. This requires
the use of a Ticker, which we'll cover in the next section.
### Tickers
Tickers are a way to schedule and synchronise computations. They're useful when
reacting to changes in atoms or prisms _outside of React's renderloop_.
```ts
import {Ticker, onChange} from '@theatre/dataverse'
const ticker = new Ticker()
// advance the ticker roughly 60 times per second (note that it's better to use requestAnimationFrame)
setInterval(ticker.tick, 1000 / 60)
onChange(atom.pointer.intensity, (newIntensity) => {
console.log('intensity changed to', newIntensity)
})
atom.setByPointer(atom.pointer.intensity, 3)
// After a few milliseconds, logs 'intensity changed to 3'
setTimeout(() => {
atom.setByPointer(atom.pointer.intensity, 4)
atom.setByPointer(atom.pointer.intensity, 5)
// updates are batched because our ticker advances every 16ms, so we
// will only get one log for 'intensity changed to 5', even though we changed the intensity twice
}, 1000)
```
Tickers should normally be advanced using `requestAnimationFrame` to make sure
all the computations are done in sync with the browser's refresh rate.
```ts
const frame = () => {
ticker.tick()
requestAnimationFrame(frame)
}
requestAnimationFrame(frame)
```
#### Benefits of using Tickers
Tickers make sure that our computations are batched and only advance atomically.
They also make sure that we don't recompute the same value twice in the same
frame.
Most importantly, Tickers allow us to align our computations to the browser's
(or the XR-device's) refresh rate.
### Prism hooks
Prism hooks are inspired by
[React hooks](https://reactjs.org/docs/hooks-intro.html). They are a convenient
way to cache, memoize, batch, and run effects inside prisms, while ensuring that
the prism can be used in a declarative, encapsulated way.
#### `prism.source()`
The `prism.source()` hook allows a prism to read to and react to changes in
values that reside outside of an atom or another prism, for example, the value
of an `<input type="text" />` element.
```ts
function prismFromInputElement(input: HTMLInputElement): Prism<string> {
function subscribe(cb: (value: string) => void) {
const listener = () => {
cb(input.value)
}
input.addEventListener('input', listener)
return () => {
input.removeEventListener('input', listener)
}
}
function get() {
return input.value
}
return prism(() => prism.source(subscribe, get))
}
const p = prismFromInputElement(document.querySelector('input'))
p.onChange(ticker, (value) => {
console.log('input value changed to', value)
})
```
#### `prism.ref()`
Just like React's `useRef()`, `prism.ref()` allows us to create a prism that
holds a reference to some value. The only difference is that `prism.ref()`
requires a key to be passed into it, whlie `useRef()` doesn't. This means that
we can call `prism.ref()` in any order, and we can call it multiple times with
the same key.
```ts
const p = prism(() => {
const inputRef = prism.ref('some-unique-key')
if (!inputRef.current) {
inputRef.current = document.$('input.username')
}
// this prism will always reflect the value of <input class="username">
return val(prismFromInputElement(inputRef.current))
})
p.onChange(ticker, (value) => {
console.log('username changed to', value)
})
```
#### `prism.memo()`
`prism.memo()` works just like React's `useMemo()` hook. It's a way to cache the
result of a function call. The only difference is that `prism.memo()` requires a
key to be passed into it, whlie `useMemo()` doesn't. This means that we can call
`prism.memo()` in any order, and we can call it multiple times with the same
key.
```ts
import {Atom, prism, val} from '@theatre/dataverse'
const atom = new Atom(0)
function factorial(n: number): number {
if (n === 0) return 1
return n * factorial(n - 1)
}
const p = prism(() => {
// num will be between 0 and 9. This is so we can test what happens when the atom's value changes, but
// the memoized value doesn't change.
const num = val(atom.pointer)
const numMod10 = num % 10
const value = prism.memo(
// we need a string key to identify the hook. This allows us to call `prism.memo()` in any order, or even conditionally.
'factorial',
// the function to memoize
() => {
console.log('Calculating factorial')
factorial(numMod10)
},
// the dependencies of the function. If any of the dependencies change, the function will be called again.
[numMod10],
)
return `number is ${num}, num % 10 is ${numMod10} and its factorial is ${value}`
})
p.onChange(ticker, (value) => {
console.log('=>', value)
})
atom.set(1)
// Calculating factorial
// => number is 1, num % 10 is 1 and its factorial is 1
atom.set(2)
// Calculating factorial
// => number is 2, num % 10 is 2 and its factorial is 2
atom.set(12) // won't recalculate the factorial
// => number is 12, num % 10 is 2 and its factorial is 2
```
#### `prism.effect()` and `prism.state()`
These are two more hooks that are similar to React's `useEffect()` and
`useState()` hooks.
`prism.effect()` is similar to React's `useEffect()` hook. It allows us to run
side-effects when the prism is calculated. Note that prisms are supposed to be
"virtually" pure functions. That means they either should not have side-effects
(and thus, no calls for `prism.effect()`), or their side-effects should clean
themselves up when the prism goes cold.
`prism.state()` is similar to React's `useState()` hook. It allows us to create
a stateful value that is scoped to the prism.
We'll defer to React's documentation for
[a more detailed explanation of how `useEffect()`](https://reactjs.org/docs/hooks-effect.html)
and how [`useState()`](https://reactjs.org/docs/hooks-state.html) work. But
here's a quick example:
```tsx
import {prism} from '@theatre/dataverse'
import {useVal} from '@theatre/react'
// This prism holds the current mouse position and updates when the mouse moves
const mousePositionPr = prism(() => {
const [pos, setPos] = prism.state<[x: number, y: number]>('pos', [0, 0])
prism.effect(
'setupListeners',
() => {
const handleMouseMove = (e: MouseEvent) => {
setPos([e.screenX, e.screenY])
}
document.addEventListener('mousemove', handleMouseMove)
return () => {
document.removeEventListener('mousemove', handleMouseMove)
}
},
[],
)
return pos
})
function Component() {
const [x, y] = useVal(mousePositionPr)
return (
<div>
Mouse position: {x}, {y}
</div>
)
}
```
#### `prism.sub()`
`prism.sub()` is a shortcut for creating a prism inside another prism. It's
equivalent to calling `prism.memo(key, () => prism(fn), deps).getValue()`.
`prism.sub()` is useful when you want to divide your prism into smaller prisms,
each of which would _only_ recalculate when _certain_ dependencies change. In
other words, it's an optimization tool.
```ts
function factorial(num: number): number {
if (num === 0) return 1
return num * factorial(num - 1)
}
const events: Array<'foo-calculated' | 'bar-calculated'> = []
// example:
const state = new Atom({foo: 0, bar: 0})
const pr = prism(() => {
const resultOfFoo = prism.sub(
'foo',
() => {
events.push('foo-calculated')
const foo = val(state.pointer.foo) % 10
// Note how `prism.sub()` is more powerful than `prism.memo()` because it allows us to use `prism.memo()` and other hooks inside of it:
return prism.memo('factorial', () => factorial(foo), [foo])
},
[],
)
const resultOfBar = prism.sub(
'bar',
() => {
events.push('bar-calculated')
const bar = val(state.pointer.bar) % 10
return prism.memo('factorial', () => factorial(bar), [bar])
},
[],
)
return `result of foo is ${resultOfFoo}, result of bar is ${resultOfBar}`
})
const unsub = pr.onChange(ticker, () => {})
// on the first run, both subs should be calculated:
console.log(events) // ['foo-calculated', 'bar-calculated']
events.length = 0 // clear the events array
// now if we change the value of `bar`, only `bar` should be recalculated:
state.setByPointer(state.pointer.bar, 2)
pr.getValue()
console.log(events) // ['bar-calculated']
unsub()
```
since prism hooks are keyed (as opposed to React hooks where they're identified
by their order), it's possible to have multiple hooks with the same key in the
same prism. To avoid this, we can use `prism.scope()` to create a "scope" for
our hooks. Example:
```ts
const pr = prism(() => {
prism.scope('a', () => {
prism.memo('foo', () => 1, [])
})
prism.scope('b', () => {
prism.memo('foo', () => 1, [])
})
})
```
### `usePrism()`
`usePrism()` is a _React_ hook that allows us to create a prism inside a React
component. This way, we can optimize our React components in a fine-grained way
by moving their computations outside of React's render loop.
```tsx
import {usePrism} from '@theatre/react'
function Component() {
const value = usePrism(() => {
// [insert heavy calculation here]
}, [])
}
```
### Hot and cold prisms
Prisms can have three states:
- 🧊 Cold: The prism was just created. It does not have dependents, or its
dependents are also 🧊 cold.
- 🔥 Hot: The prism is either being subscribed to (via `useVal()`,
`prism.onChange()`, `prism.onStale()`, etc). Or, one of its dependents is 🔥
hot.
- A 🔥 Hot prism itself has two states:
- 🪵 Stale: The prism is hot, but its value is stale. This happens when one
or more of its dependencies have changed, but the value of the prism
hasn't been read since that change. Reading the value of a 🪵 Stale prism
will cause it to recalculate, and make it 🌲 Fresh.
- 🌲 Fresh: The prism is hot, and its value is fresh. This happens when the
prism's value has been read since the last change in its dependencies.
Re-reading the value of a 🌲 Fresh prism will _not_ cause it to
recalculate.
Or, as a typescript annotation:
```ts
type PrismState =
| {isHot: false} // 🧊
| {isHot: true; isFresh: false} // 🔥🪵
| {isHot: true; isFresh: true} // 🔥🌲
```
Let's demonstrate this with an example of a prism, and its `onStale()` method.
```ts
const atom = new Atom(0)
const a = prism(() => val(atom.pointer)) // 🧊
// onStale(cb) calls `cb` when the prism goes from 🌲 to 🪵
a.onStale(() => {
console.log('a is stale')
})
// a from 🧊 to 🔥
// console: a is stale
// reading the value of `a` will cause it to recalculate, and make it 🌲 fresh.
console.log(val(a)) // 1
// a from 🔥🪵 to 🔥🌲
atom.set(1)
// a from 🔥🌲 to 🔥🪵
// console: a is stale
// reading the value of `a` will cause it to recalculate, and make it 🌲 fresh.
console.log(val(a)) // 2
```
Prism states propogate through the prism dependency graph. Let's look at an
example:
```ts
const atom = new Atom({a: 0, b: 0})
const a = prism(() => val(atom.pointer.a))
const b = prism(() => val(atom.pointer.b))
const sum = prism(() => val(a) + val(b))
// a | b | sum |
// 🧊 | 🧊 | 🧊 |
let unsub = a.onStale(() => {})
// there is now a subscription to `a`, so it's 🔥 hot
// a | b | sum |
// 🔥🪵 | 🧊 | 🧊 |
unsub()
// there are no subscriptions to `a`, so it's 🧊 cold again
// a | b | sum |
// 🧊 | 🧊 | 🧊 |
unsub = sum.onStale(() => {})
// there is now a subscription to `sum`, so it goes 🔥 hot, and so do its dependencies
// a | b | sum |
// 🔥🪵 | 🔥🪵 | 🔥🪵 |
val(sum)
// reading the value of `sum` will cause it to recalculate, and make it 🌲 fresh.
// a | b | sum |
// 🔥🌲 | 🔥🌲 | 🔥🌲 |
atom.setByPointer(atom.pointer.a, 1)
// `a` is now stale, which will cause `sum` to become stale as well
// a | b | sum |
// 🔥🪵 | 🔥🌲 | 🔥🪵 |
val(a)
// reading the value of `a` will cause it to recalculate, and make it 🌲 fresh. But notice that `sum` is still 🪵 stale.
// a | b | sum |
// 🔥🌲 | 🔥🌲 | 🔥🪵 |
atom.setByPointer(atom.pointer.b, 1)
// `b` now goes stale. Since sum was already stale, it will remain so
// a | b | sum |
// 🔥🌲 | 🔥🪵 | 🔥🪵 |
val(sum)
// reading the value of `sum` will cause it to recalculate and go 🌲 fresh.
// a | b | sum |
// 🔥🌲 | 🔥🌲 | 🔥🌲 |
unsub()
// there are no subscriptions to `sum`, so it goes 🧊 cold again, and so do its dependencies, since they don't have any other hot dependents
// a | b | sum |
// 🧊 | 🧊 | 🧊 |
```
The state transitions propogate in topological order. Let's demonstrate this by
adding one more prism to our dependency graph:
```ts
// continued from the previous example
const double = prism(() => val(sum) * 2)
// Initially, all prisms are 🧊 cold
// a | b | sum | double |
// 🧊 | 🧊 | 🧊 | 🧊 |
let unsub = double.onStale(() => {})
// here is how the state transitions will happen, step by step:
// (step) | a | b | sum | double |
// 1 | 🧊 | 🧊 | 🧊 | 🔥🪵 |
// 2 | 🧊 | 🧊 | 🔥🪵 | 🔥🪵 |
// 3 | 🔥🪵 | 🔥🪵 | 🔥🪵 | 🔥🪵 |
val(double)
// freshening happens in the reverse order
// (step) | a | b | sum | double |
// 0 | 🔥🪵 | 🔥🪵 | 🔥🪵 | 🔥🪵 |
// --------------------------------------------------|
// 1 ▲ ▼ | double reads the value of sum
// └────◄────┘ |
// --------------------------------------------------|
// 2 ▲ ▲ ▼ | sum reads the value of a and b
// │ │ │ |
// └────◄───┴────◄─────┘ |
// --------------------------------------------------|
// 3 | 🔥🌲 | 🔥🌲 | 🔥🪵 | 🔥🪵 | a and b go fresh
// --------------------------------------------------|
// 4 | 🔥🌲 | 🔥🌲 | 🔥🌲 | 🔥🪵 | sum goes fresh
// --------------------------------------------------|
// 5 | 🔥🌲 | 🔥🌲 | 🔥🌲 | 🔥🌲 | double goes fresh
// --------------------------------------------------|
```
## Links
- [API Reference](./api/README.md)
- [The exhaustive guide to dataverse](./src/dataverse.test.ts)
- It's also fun to
[open the monorepo](https://github1s.com/theatre-js/theatre/blob/main/packages/dataverse/src/index.ts)
in VSCode and look up references to `Atom`, `prism()` and other dataverse
methods. Since dataverse is used internally in Theatre.js, there are a lot of
examples of how to use it.
- Also see [`@theatre/react`](../react/README.md) to learn more about the React
bindings.

View file

@ -0,0 +1 @@
TypeDoc added this file to prevent GitHub Pages from using Jekyll. You can turn off this behavior by setting the `githubPages` option to false.

View file

@ -0,0 +1,402 @@
@theatre/dataverse
# @theatre/dataverse
The animation-optimized FRP library powering the internals of Theatre.js.
## Table of contents
### Namespaces
- [prism](modules/prism.md)
### Classes
- [Atom](classes/Atom.md)
- [PointerProxy](classes/PointerProxy.md)
- [Ticker](classes/Ticker.md)
### Interfaces
- [PointerToPrismProvider](interfaces/PointerToPrismProvider.md)
- [Prism](interfaces/Prism-1.md)
### Type Aliases
- [Pointer](README.md#pointer)
- [PointerMeta](README.md#pointermeta)
- [PointerType](README.md#pointertype)
### Functions
- [getPointerParts](README.md#getpointerparts)
- [isPointer](README.md#ispointer)
- [isPrism](README.md#isprism)
- [iterateAndCountTicks](README.md#iterateandcountticks)
- [iterateOver](README.md#iterateover)
- [pointer](README.md#pointer-1)
- [pointerToPrism](README.md#pointertoprism)
- [prism](README.md#prism)
- [val](README.md#val)
## Type Aliases
### Pointer
Ƭ **Pointer**<`O`\>: [`PointerType`](README.md#pointertype)<`O`\> & `PointerInner`<`Exclude`<`O`, `undefined`\>, `undefined` extends `O` ? `undefined` : `never`\>
The type of [Atom](classes/Atom.md) pointers. See [pointer()](README.md#pointer-1) for an
explanation of pointers.
**`See`**
Atom
**`Remarks`**
The Pointer type is quite tricky because it doesn't play well with `any` and other inexact types.
Here is an example that one would expect to work, but currently doesn't:
```ts
declare function expectAnyPointer(pointer: Pointer<any>): void
expectAnyPointer(null as Pointer<{}>) // this shows as a type error because Pointer<{}> is not assignable to Pointer<any>, even though it should
```
The current solution is to just avoid using `any` with pointer-related code (or type-test it well).
But if you enjoy solving typescript puzzles, consider fixing this :)
Potentially, [TypeScript variance annotations in 4.7+](https://devblogs.microsoft.com/typescript/announcing-typescript-4-7-beta/#optional-variance-annotations-for-type-parameters)
might be able to help us.
#### Type parameters
| Name |
| :------ |
| `O` |
#### Defined in
[pointer.ts:64](https://github.com/theatre-js/theatre/blob/327b859ed/packages/dataverse/src/pointer.ts#L64)
___
### PointerMeta
Ƭ **PointerMeta**: `Object`
#### Type declaration
| Name | Type |
| :------ | :------ |
| `path` | (`string` \| `number`)[] |
| `root` | {} |
#### Defined in
[pointer.ts:5](https://github.com/theatre-js/theatre/blob/327b859ed/packages/dataverse/src/pointer.ts#L5)
___
### PointerType
Ƭ **PointerType**<`O`\>: `Object`
A wrapper type for the type a `Pointer` points to.
#### Type parameters
| Name |
| :------ |
| `O` |
#### Type declaration
| Name | Type | Description |
| :------ | :------ | :------ |
| `$$__pointer_type` | `O` | Only accessible via the type system. This is a helper for getting the underlying pointer type via the type space. |
#### Defined in
[pointer.ts:35](https://github.com/theatre-js/theatre/blob/327b859ed/packages/dataverse/src/pointer.ts#L35)
## Functions
### getPointerParts
**getPointerParts**<`_`\>(`p`): `Object`
Returns the root object and the path of the pointer.
#### Type parameters
| Name |
| :------ |
| `_` |
#### Parameters
| Name | Type | Description |
| :------ | :------ | :------ |
| `p` | [`Pointer`](README.md#pointer)<`_`\> | The pointer. |
#### Returns
`Object`
An object with two properties: `root`-the root object or the pointer, and `path`-the path of the pointer. `path` is an array of the property-chain.
| Name | Type |
| :------ | :------ |
| `path` | `PathToProp` |
| `root` | {} |
**`Example`**
```ts
const {root, path} = getPointerParts(pointer)
```
#### Defined in
[pointer.ts:136](https://github.com/theatre-js/theatre/blob/327b859ed/packages/dataverse/src/pointer.ts#L136)
___
### isPointer
**isPointer**(`p`): p is Pointer<unknown\>
Returns whether `p` is a pointer.
#### Parameters
| Name | Type |
| :------ | :------ |
| `p` | `any` |
#### Returns
p is Pointer<unknown\>
#### Defined in
[pointer.ts:187](https://github.com/theatre-js/theatre/blob/327b859ed/packages/dataverse/src/pointer.ts#L187)
___
### isPrism
**isPrism**(`d`): d is Prism<unknown\>
Returns whether `d` is a prism.
#### Parameters
| Name | Type |
| :------ | :------ |
| `d` | `any` |
#### Returns
d is Prism<unknown\>
#### Defined in
[prism/Interface.ts:66](https://github.com/theatre-js/theatre/blob/327b859ed/packages/dataverse/src/prism/Interface.ts#L66)
___
### iterateAndCountTicks
**iterateAndCountTicks**<`V`\>(`pointerOrPrism`): `Generator`<{ `ticks`: `number` ; `value`: `V` }, `void`, `void`\>
#### Type parameters
| Name |
| :------ |
| `V` |
#### Parameters
| Name | Type |
| :------ | :------ |
| `pointerOrPrism` | [`Prism`](interfaces/Prism-1.md)<`V`\> \| [`Pointer`](README.md#pointer)<`V`\> |
#### Returns
`Generator`<{ `ticks`: `number` ; `value`: `V` }, `void`, `void`\>
#### Defined in
[prism/iterateAndCountTicks.ts:7](https://github.com/theatre-js/theatre/blob/327b859ed/packages/dataverse/src/prism/iterateAndCountTicks.ts#L7)
___
### iterateOver
**iterateOver**<`V`\>(`pointerOrPrism`): `Generator`<`V`, `void`, `void`\>
#### Type parameters
| Name |
| :------ |
| `V` |
#### Parameters
| Name | Type |
| :------ | :------ |
| `pointerOrPrism` | [`Prism`](interfaces/Prism-1.md)<`V`\> \| [`Pointer`](README.md#pointer)<`V`\> |
#### Returns
`Generator`<`V`, `void`, `void`\>
#### Defined in
[prism/iterateOver.ts:8](https://github.com/theatre-js/theatre/blob/327b859ed/packages/dataverse/src/prism/iterateOver.ts#L8)
___
### pointer
**pointer**<`O`\>(`args`): [`Pointer`](README.md#pointer)<`O`\>
Creates a pointer to a (nested) property of an [Atom](classes/Atom.md).
#### Type parameters
| Name | Description |
| :------ | :------ |
| `O` | The type of the value being pointed to. |
#### Parameters
| Name | Type | Description |
| :------ | :------ | :------ |
| `args` | `Object` | The pointer parameters. |
| `args.path?` | (`string` \| `number`)[] | - |
| `args.root` | `Object` | - |
#### Returns
[`Pointer`](README.md#pointer)<`O`\>
**`Remarks`**
Pointers are used to make prisms of properties or nested properties of
Atom|Atoms.
Pointers also allow easy construction of new pointers pointing to nested members
of the root object, by simply using property chaining. E.g. `somePointer.a.b` will
create a new pointer that has `'a'` and `'b'` added to the path of `somePointer`.
**`Example`**
```ts
// Here, sum is a prism that updates whenever the a or b prop of someAtom does.
const sum = prism(() => {
return val(pointer({root: someAtom, path: ['a']})) + val(pointer({root: someAtom, path: ['b']}));
});
// Note, atoms have a convenience Atom.pointer property that points to the root,
// which you would normally use in this situation.
const sum = prism(() => {
return val(someAtom.pointer.a) + val(someAtom.pointer.b);
});
```
#### Defined in
[pointer.ts:172](https://github.com/theatre-js/theatre/blob/327b859ed/packages/dataverse/src/pointer.ts#L172)
___
### pointerToPrism
**pointerToPrism**<`P`\>(`pointer`): [`Prism`](interfaces/Prism-1.md)<`P` extends [`PointerType`](README.md#pointertype)<`T`\> ? `T` : `void`\>
Returns a prism of the value at the provided pointer. Prisms are
cached per pointer.
#### Type parameters
| Name | Type |
| :------ | :------ |
| `P` | extends [`PointerType`](README.md#pointertype)<`any`\> |
#### Parameters
| Name | Type | Description |
| :------ | :------ | :------ |
| `pointer` | `P` | The pointer to return the prism at. |
#### Returns
[`Prism`](interfaces/Prism-1.md)<`P` extends [`PointerType`](README.md#pointertype)<`T`\> ? `T` : `void`\>
#### Defined in
[pointerToPrism.ts:41](https://github.com/theatre-js/theatre/blob/327b859ed/packages/dataverse/src/pointerToPrism.ts#L41)
___
### prism
**prism**<`T`\>(`fn`): [`Prism`](interfaces/Prism-1.md)<`T`\>
Creates a prism from the passed function that adds all prisms referenced
in it as dependencies, and reruns the function when these change.
#### Type parameters
| Name |
| :------ |
| `T` |
#### Parameters
| Name | Type | Description |
| :------ | :------ | :------ |
| `fn` | () => `T` | The function to rerun when the prisms referenced in it change. |
#### Returns
[`Prism`](interfaces/Prism-1.md)<`T`\>
#### Defined in
[prism/prism.ts:817](https://github.com/theatre-js/theatre/blob/327b859ed/packages/dataverse/src/prism/prism.ts#L817)
___
### val
**val**<`P`\>(`input`): `P` extends [`PointerType`](README.md#pointertype)<`T`\> ? `T` : `P` extends [`Prism`](interfaces/Prism-1.md)<`T`\> ? `T` : `P` extends `undefined` \| ``null`` ? `P` : `unknown`
Convenience function that returns a plain value from its argument, whether it
is a pointer, a prism or a plain value itself.
#### Type parameters
| Name | Type |
| :------ | :------ |
| `P` | extends `undefined` \| ``null`` \| [`Prism`](interfaces/Prism-1.md)<`any`\> \| [`PointerType`](README.md#pointertype)<`any`\> |
#### Parameters
| Name | Type | Description |
| :------ | :------ | :------ |
| `input` | `P` | The argument to return a value from. |
#### Returns
`P` extends [`PointerType`](README.md#pointertype)<`T`\> ? `T` : `P` extends [`Prism`](interfaces/Prism-1.md)<`T`\> ? `T` : `P` extends `undefined` \| ``null`` ? `P` : `unknown`
**`Remarks`**
For pointers, the value is returned by first creating a prism, so it is
reactive e.g. when used in a `prism`.
#### Defined in
[val.ts:19](https://github.com/theatre-js/theatre/blob/327b859ed/packages/dataverse/src/val.ts#L19)

View file

@ -0,0 +1,381 @@
[@theatre/dataverse](../README.md) / Atom
# Class: Atom<State\>
Wraps an object whose (sub)properties can be individually tracked.
## Type parameters
| Name |
| :------ |
| `State` |
## Implements
- [`PointerToPrismProvider`](../interfaces/PointerToPrismProvider.md)
## Table of contents
### Constructors
- [constructor](Atom.md#constructor)
### Properties
- [\_currentState](Atom.md#_currentstate)
- [\_rootScope](Atom.md#_rootscope)
- [pointer](Atom.md#pointer)
- [prism](Atom.md#prism)
### Methods
- [\_checkUpdates](Atom.md#_checkupdates)
- [\_getIn](Atom.md#_getin)
- [\_getOrCreateScopeForPath](Atom.md#_getorcreatescopeforpath)
- [\_onPointerValueChange](Atom.md#_onpointervaluechange)
- [get](Atom.md#get)
- [getByPointer](Atom.md#getbypointer)
- [pointerToPrism](Atom.md#pointertoprism)
- [reduce](Atom.md#reduce)
- [reduceByPointer](Atom.md#reducebypointer)
- [set](Atom.md#set)
- [setByPointer](Atom.md#setbypointer)
## Constructors
### constructor
**new Atom**<`State`\>(`initialState`)
#### Type parameters
| Name |
| :------ |
| `State` |
#### Parameters
| Name | Type |
| :------ | :------ |
| `initialState` | `State` |
#### Defined in
[Atom.ts:119](https://github.com/theatre-js/theatre/blob/327b859ed/packages/dataverse/src/Atom.ts#L119)
## Properties
### \_currentState
`Private` **\_currentState**: `State`
#### Defined in
[Atom.ts:101](https://github.com/theatre-js/theatre/blob/327b859ed/packages/dataverse/src/Atom.ts#L101)
___
### \_rootScope
`Private` `Readonly` **\_rootScope**: `Scope`
#### Defined in
[Atom.ts:106](https://github.com/theatre-js/theatre/blob/327b859ed/packages/dataverse/src/Atom.ts#L106)
___
### pointer
`Readonly` **pointer**: [`Pointer`](../README.md#pointer)<`State`\>
Convenience property that gives you a pointer to the root of the atom.
**`Remarks`**
Equivalent to `pointer({ root: thisAtom, path: [] })`.
#### Defined in
[Atom.ts:113](https://github.com/theatre-js/theatre/blob/327b859ed/packages/dataverse/src/Atom.ts#L113)
___
### prism
`Readonly` **prism**: [`Prism`](../interfaces/Prism-1.md)<`State`\>
#### Defined in
[Atom.ts:115](https://github.com/theatre-js/theatre/blob/327b859ed/packages/dataverse/src/Atom.ts#L115)
## Methods
### \_checkUpdates
`Private` **_checkUpdates**(`scope`, `oldState`, `newState`): `void`
#### Parameters
| Name | Type |
| :------ | :------ |
| `scope` | `Scope` |
| `oldState` | `unknown` |
| `newState` | `unknown` |
#### Returns
`void`
#### Defined in
[Atom.ts:218](https://github.com/theatre-js/theatre/blob/327b859ed/packages/dataverse/src/Atom.ts#L218)
___
### \_getIn
`Private` **_getIn**(`path`): `unknown`
Gets the state of the atom at `path`.
#### Parameters
| Name | Type |
| :------ | :------ |
| `path` | (`string` \| `number`)[] |
#### Returns
`unknown`
#### Defined in
[Atom.ts:166](https://github.com/theatre-js/theatre/blob/327b859ed/packages/dataverse/src/Atom.ts#L166)
___
### \_getOrCreateScopeForPath
`Private` **_getOrCreateScopeForPath**(`path`): `Scope`
#### Parameters
| Name | Type |
| :------ | :------ |
| `path` | (`string` \| `number`)[] |
#### Returns
`Scope`
#### Defined in
[Atom.ts:240](https://github.com/theatre-js/theatre/blob/327b859ed/packages/dataverse/src/Atom.ts#L240)
___
### \_onPointerValueChange
`Private` **_onPointerValueChange**<`P`\>(`pointer`, `cb`): () => `void`
#### Type parameters
| Name |
| :------ |
| `P` |
#### Parameters
| Name | Type |
| :------ | :------ |
| `pointer` | [`Pointer`](../README.md#pointer)<`P`\> |
| `cb` | (`v`: `P`) => `void` |
#### Returns
`fn`
▸ (): `void`
##### Returns
`void`
#### Defined in
[Atom.ts:248](https://github.com/theatre-js/theatre/blob/327b859ed/packages/dataverse/src/Atom.ts#L248)
___
### get
**get**(): `State`
#### Returns
`State`
#### Defined in
[Atom.ts:136](https://github.com/theatre-js/theatre/blob/327b859ed/packages/dataverse/src/Atom.ts#L136)
___
### getByPointer
**getByPointer**<`S`\>(`pointerOrFn`): `S`
Returns the value at the given pointer
#### Type parameters
| Name |
| :------ |
| `S` |
#### Parameters
| Name | Type | Description |
| :------ | :------ | :------ |
| `pointerOrFn` | [`Pointer`](../README.md#pointer)<`S`\> \| (`p`: [`Pointer`](../README.md#pointer)<`State`\>) => [`Pointer`](../README.md#pointer)<`S`\> | A pointer to the desired path. Could also be a function returning a pointer Example ```ts const atom = atom({ a: { b: 1 } }) atom.getByPointer(atom.pointer.a.b) // 1 atom.getByPointer((p) => p.a.b) // 1 ``` |
#### Returns
`S`
#### Defined in
[Atom.ts:152](https://github.com/theatre-js/theatre/blob/327b859ed/packages/dataverse/src/Atom.ts#L152)
___
### pointerToPrism
**pointerToPrism**<`P`\>(`pointer`): [`Prism`](../interfaces/Prism-1.md)<`P`\>
Returns a new prism of the value at the provided path.
#### Type parameters
| Name |
| :------ |
| `P` |
#### Parameters
| Name | Type | Description |
| :------ | :------ | :------ |
| `pointer` | [`Pointer`](../README.md#pointer)<`P`\> | The path to create the prism at. ```ts const pr = atom({ a: { b: 1 } }).pointerToPrism(atom.pointer.a.b) pr.getValue() // 1 ``` |
#### Returns
[`Prism`](../interfaces/Prism-1.md)<`P`\>
#### Implementation of
[PointerToPrismProvider](../interfaces/PointerToPrismProvider.md).[pointerToPrism](../interfaces/PointerToPrismProvider.md#pointertoprism)
#### Defined in
[Atom.ts:271](https://github.com/theatre-js/theatre/blob/327b859ed/packages/dataverse/src/Atom.ts#L271)
___
### reduce
**reduce**(`fn`): `void`
#### Parameters
| Name | Type |
| :------ | :------ |
| `fn` | (`state`: `State`) => `State` |
#### Returns
`void`
#### Defined in
[Atom.ts:170](https://github.com/theatre-js/theatre/blob/327b859ed/packages/dataverse/src/Atom.ts#L170)
___
### reduceByPointer
**reduceByPointer**<`S`\>(`pointerOrFn`, `reducer`): `void`
Reduces the value at the given pointer
#### Type parameters
| Name |
| :------ |
| `S` |
#### Parameters
| Name | Type | Description |
| :------ | :------ | :------ |
| `pointerOrFn` | [`Pointer`](../README.md#pointer)<`S`\> \| (`p`: [`Pointer`](../README.md#pointer)<`State`\>) => [`Pointer`](../README.md#pointer)<`S`\> | A pointer to the desired path. Could also be a function returning a pointer Example ```ts const atom = atom({ a: { b: 1 } }) atom.reduceByPointer(atom.pointer.a.b, (b) => b + 1) // atom.get().a.b === 2 atom.reduceByPointer((p) => p.a.b, (b) => b + 1) // atom.get().a.b === 2 ``` |
| `reducer` | (`s`: `S`) => `S` | - |
#### Returns
`void`
#### Defined in
[Atom.ts:186](https://github.com/theatre-js/theatre/blob/327b859ed/packages/dataverse/src/Atom.ts#L186)
___
### set
**set**(`newState`): `void`
Sets the state of the atom.
#### Parameters
| Name | Type | Description |
| :------ | :------ | :------ |
| `newState` | `State` | The new state of the atom. |
#### Returns
`void`
#### Defined in
[Atom.ts:129](https://github.com/theatre-js/theatre/blob/327b859ed/packages/dataverse/src/Atom.ts#L129)
___
### setByPointer
**setByPointer**<`S`\>(`pointerOrFn`, `val`): `void`
Sets the value at the given pointer
#### Type parameters
| Name |
| :------ |
| `S` |
#### Parameters
| Name | Type | Description |
| :------ | :------ | :------ |
| `pointerOrFn` | [`Pointer`](../README.md#pointer)<`S`\> \| (`p`: [`Pointer`](../README.md#pointer)<`State`\>) => [`Pointer`](../README.md#pointer)<`S`\> | A pointer to the desired path. Could also be a function returning a pointer Example ```ts const atom = atom({ a: { b: 1 } }) atom.setByPointer(atom.pointer.a.b, 2) // atom.get().a.b === 2 atom.setByPointer((p) => p.a.b, 2) // atom.get().a.b === 2 ``` |
| `val` | `S` | - |
#### Returns
`void`
#### Defined in
[Atom.ts:211](https://github.com/theatre-js/theatre/blob/327b859ed/packages/dataverse/src/Atom.ts#L211)

View file

@ -0,0 +1,138 @@
[@theatre/dataverse](../README.md) / PointerProxy
# Class: PointerProxy<O\>
Allows creating pointer-prisms where the pointer can be switched out.
**`Remarks`**
This allows reacting not just to value changes at a certain pointer, but changes
to the proxied pointer too.
## Type parameters
| Name | Type |
| :------ | :------ |
| `O` | extends `Object` |
## Implements
- [`PointerToPrismProvider`](../interfaces/PointerToPrismProvider.md)
## Table of contents
### Constructors
- [constructor](PointerProxy.md#constructor)
### Properties
- [\_currentPointerBox](PointerProxy.md#_currentpointerbox)
- [pointer](PointerProxy.md#pointer)
### Methods
- [pointerToPrism](PointerProxy.md#pointertoprism)
- [setPointer](PointerProxy.md#setpointer)
## Constructors
### constructor
**new PointerProxy**<`O`\>(`currentPointer`)
#### Type parameters
| Name | Type |
| :------ | :------ |
| `O` | extends `Object` |
#### Parameters
| Name | Type |
| :------ | :------ |
| `currentPointer` | [`Pointer`](../README.md#pointer)<`O`\> |
#### Defined in
[PointerProxy.ts:34](https://github.com/theatre-js/theatre/blob/327b859ed/packages/dataverse/src/PointerProxy.ts#L34)
## Properties
### \_currentPointerBox
`Private` `Readonly` **\_currentPointerBox**: [`Atom`](Atom.md)<[`Pointer`](../README.md#pointer)<`O`\>\>
#### Defined in
[PointerProxy.ts:25](https://github.com/theatre-js/theatre/blob/327b859ed/packages/dataverse/src/PointerProxy.ts#L25)
___
### pointer
`Readonly` **pointer**: [`Pointer`](../README.md#pointer)<`O`\>
Convenience pointer pointing to the root of this PointerProxy.
**`Remarks`**
Allows convenient use of [pointerToPrism](PointerProxy.md#pointertoprism) and [val](../README.md#val).
#### Defined in
[PointerProxy.ts:32](https://github.com/theatre-js/theatre/blob/327b859ed/packages/dataverse/src/PointerProxy.ts#L32)
## Methods
### pointerToPrism
**pointerToPrism**<`P`\>(`pointer`): [`Prism`](../interfaces/Prism-1.md)<`P`\>
Returns a prism of the value at the provided sub-path of the proxied pointer.
#### Type parameters
| Name |
| :------ |
| `P` |
#### Parameters
| Name | Type |
| :------ | :------ |
| `pointer` | [`Pointer`](../README.md#pointer)<`P`\> |
#### Returns
[`Prism`](../interfaces/Prism-1.md)<`P`\>
#### Implementation of
[PointerToPrismProvider](../interfaces/PointerToPrismProvider.md).[pointerToPrism](../interfaces/PointerToPrismProvider.md#pointertoprism)
#### Defined in
[PointerProxy.ts:52](https://github.com/theatre-js/theatre/blob/327b859ed/packages/dataverse/src/PointerProxy.ts#L52)
___
### setPointer
**setPointer**(`p`): `void`
Sets the underlying pointer.
#### Parameters
| Name | Type | Description |
| :------ | :------ | :------ |
| `p` | [`Pointer`](../README.md#pointer)<`O`\> | The pointer to be proxied. |
#### Returns
`void`
#### Defined in
[PointerProxy.ts:43](https://github.com/theatre-js/theatre/blob/327b859ed/packages/dataverse/src/PointerProxy.ts#L43)

View file

@ -0,0 +1,372 @@
[@theatre/dataverse](../README.md) / Ticker
# Class: Ticker
The Ticker class helps schedule callbacks. Scheduled callbacks are executed per tick. Ticks can be triggered by an
external scheduling strategy, e.g. a raf.
## Table of contents
### Constructors
- [constructor](Ticker.md#constructor)
### Properties
- [\_\_ticks](Ticker.md#__ticks)
- [\_conf](Ticker.md#_conf)
- [\_dormant](Ticker.md#_dormant)
- [\_numberOfDormantTicks](Ticker.md#_numberofdormantticks)
- [\_scheduledForNextTick](Ticker.md#_scheduledfornexttick)
- [\_scheduledForThisOrNextTick](Ticker.md#_scheduledforthisornexttick)
- [\_ticking](Ticker.md#_ticking)
- [\_timeAtCurrentTick](Ticker.md#_timeatcurrenttick)
### Accessors
- [dormant](Ticker.md#dormant)
- [time](Ticker.md#time)
### Methods
- [\_goActive](Ticker.md#_goactive)
- [\_goDormant](Ticker.md#_godormant)
- [\_tick](Ticker.md#_tick)
- [offNextTick](Ticker.md#offnexttick)
- [offThisOrNextTick](Ticker.md#offthisornexttick)
- [onNextTick](Ticker.md#onnexttick)
- [onThisOrNextTick](Ticker.md#onthisornexttick)
- [tick](Ticker.md#tick)
## Constructors
### constructor
**new Ticker**(`_conf?`)
#### Parameters
| Name | Type |
| :------ | :------ |
| `_conf?` | `Object` |
| `_conf.onActive?` | () => `void` |
| `_conf.onDormant?` | () => `void` |
#### Defined in
[Ticker.ts:43](https://github.com/theatre-js/theatre/blob/327b859ed/packages/dataverse/src/Ticker.ts#L43)
## Properties
### \_\_ticks
**\_\_ticks**: `number` = `0`
Counts up for every tick executed.
Internally, this is used to measure ticks per second.
This is "public" to TypeScript, because it's a tool for performance measurements.
Consider this as experimental, and do not rely on it always being here in future releases.
#### Defined in
[Ticker.ts:41](https://github.com/theatre-js/theatre/blob/327b859ed/packages/dataverse/src/Ticker.ts#L41)
___
### \_conf
`Private` `Optional` **\_conf**: `Object`
#### Type declaration
| Name | Type |
| :------ | :------ |
| `onActive?` | () => `void` |
| `onDormant?` | () => `void` |
#### Defined in
[Ticker.ts:44](https://github.com/theatre-js/theatre/blob/327b859ed/packages/dataverse/src/Ticker.ts#L44)
___
### \_dormant
`Private` **\_dormant**: `boolean` = `true`
Whether the Ticker is dormant
#### Defined in
[Ticker.ts:24](https://github.com/theatre-js/theatre/blob/327b859ed/packages/dataverse/src/Ticker.ts#L24)
___
### \_numberOfDormantTicks
`Private` **\_numberOfDormantTicks**: `number` = `0`
#### Defined in
[Ticker.ts:26](https://github.com/theatre-js/theatre/blob/327b859ed/packages/dataverse/src/Ticker.ts#L26)
___
### \_scheduledForNextTick
`Private` **\_scheduledForNextTick**: `Set`<`ICallback`\>
#### Defined in
[Ticker.ts:17](https://github.com/theatre-js/theatre/blob/327b859ed/packages/dataverse/src/Ticker.ts#L17)
___
### \_scheduledForThisOrNextTick
`Private` **\_scheduledForThisOrNextTick**: `Set`<`ICallback`\>
#### Defined in
[Ticker.ts:16](https://github.com/theatre-js/theatre/blob/327b859ed/packages/dataverse/src/Ticker.ts#L16)
___
### \_ticking
`Private` **\_ticking**: `boolean` = `false`
#### Defined in
[Ticker.ts:19](https://github.com/theatre-js/theatre/blob/327b859ed/packages/dataverse/src/Ticker.ts#L19)
___
### \_timeAtCurrentTick
`Private` **\_timeAtCurrentTick**: `number`
#### Defined in
[Ticker.ts:18](https://github.com/theatre-js/theatre/blob/327b859ed/packages/dataverse/src/Ticker.ts#L18)
## Accessors
### dormant
`get` **dormant**(): `boolean`
Whether the Ticker is dormant
#### Returns
`boolean`
#### Defined in
[Ticker.ts:31](https://github.com/theatre-js/theatre/blob/327b859ed/packages/dataverse/src/Ticker.ts#L31)
___
### time
`get` **time**(): `number`
The time at the start of the current tick if there is a tick in progress, otherwise defaults to
`performance.now()`.
#### Returns
`number`
#### Defined in
[Ticker.ts:122](https://github.com/theatre-js/theatre/blob/327b859ed/packages/dataverse/src/Ticker.ts#L122)
## Methods
### \_goActive
`Private` **_goActive**(): `void`
#### Returns
`void`
#### Defined in
[Ticker.ts:128](https://github.com/theatre-js/theatre/blob/327b859ed/packages/dataverse/src/Ticker.ts#L128)
___
### \_goDormant
`Private` **_goDormant**(): `void`
#### Returns
`void`
#### Defined in
[Ticker.ts:134](https://github.com/theatre-js/theatre/blob/327b859ed/packages/dataverse/src/Ticker.ts#L134)
___
### \_tick
`Private` **_tick**(`iterationNumber`): `void`
#### Parameters
| Name | Type |
| :------ | :------ |
| `iterationNumber` | `number` |
#### Returns
`void`
#### Defined in
[Ticker.ts:184](https://github.com/theatre-js/theatre/blob/327b859ed/packages/dataverse/src/Ticker.ts#L184)
___
### offNextTick
**offNextTick**(`fn`): `void`
De-registers a fn to be called on the next tick.
#### Parameters
| Name | Type | Description |
| :------ | :------ | :------ |
| `fn` | `ICallback` | The function to be de-registered. |
#### Returns
`void`
**`See`**
onNextTick
#### Defined in
[Ticker.ts:114](https://github.com/theatre-js/theatre/blob/327b859ed/packages/dataverse/src/Ticker.ts#L114)
___
### offThisOrNextTick
**offThisOrNextTick**(`fn`): `void`
De-registers a fn to be called either on this tick or the next tick.
#### Parameters
| Name | Type | Description |
| :------ | :------ | :------ |
| `fn` | `ICallback` | The function to be de-registered. |
#### Returns
`void`
**`See`**
onThisOrNextTick
#### Defined in
[Ticker.ts:103](https://github.com/theatre-js/theatre/blob/327b859ed/packages/dataverse/src/Ticker.ts#L103)
___
### onNextTick
**onNextTick**(`fn`): `void`
Registers a side effect to be called on the next tick.
#### Parameters
| Name | Type | Description |
| :------ | :------ | :------ |
| `fn` | `ICallback` | The function to be registered. |
#### Returns
`void`
**`See`**
- onThisOrNextTick
- offNextTick
#### Defined in
[Ticker.ts:89](https://github.com/theatre-js/theatre/blob/327b859ed/packages/dataverse/src/Ticker.ts#L89)
___
### onThisOrNextTick
**onThisOrNextTick**(`fn`): `void`
Registers for fn to be called either on this tick or the next tick.
If `onThisOrNextTick()` is called while `Ticker.tick()` is running, the
side effect _will_ be called within the running tick. If you don't want this
behavior, you can use `onNextTick()`.
Note that `fn` will be added to a `Set()`. Which means, if you call `onThisOrNextTick(fn)`
with the same fn twice in a single tick, it'll only run once.
#### Parameters
| Name | Type | Description |
| :------ | :------ | :------ |
| `fn` | `ICallback` | The function to be registered. |
#### Returns
`void`
**`See`**
offThisOrNextTick
#### Defined in
[Ticker.ts:74](https://github.com/theatre-js/theatre/blob/327b859ed/packages/dataverse/src/Ticker.ts#L74)
___
### tick
**tick**(`t?`): `void`
Triggers a tick which starts executing the callbacks scheduled for this tick.
#### Parameters
| Name | Type | Description |
| :------ | :------ | :------ |
| `t` | `number` | The time at the tick. |
#### Returns
`void`
**`See`**
- onThisOrNextTick
- onNextTick
#### Defined in
[Ticker.ts:149](https://github.com/theatre-js/theatre/blob/327b859ed/packages/dataverse/src/Ticker.ts#L149)

View file

@ -0,0 +1,44 @@
[@theatre/dataverse](../README.md) / PointerToPrismProvider
# Interface: PointerToPrismProvider
Interface for objects that can provide a prism at a certain path.
## Implemented by
- [`Atom`](../classes/Atom.md)
- [`PointerProxy`](../classes/PointerProxy.md)
## Table of contents
### Methods
- [pointerToPrism](PointerToPrismProvider.md#pointertoprism)
## Methods
### pointerToPrism
**pointerToPrism**<`P`\>(`pointer`): [`Prism`](Prism-1.md)<`P`\>
Returns a prism of the value at the provided pointer.
#### Type parameters
| Name |
| :------ |
| `P` |
#### Parameters
| Name | Type |
| :------ | :------ |
| `pointer` | [`Pointer`](../README.md#pointer)<`P`\> |
#### Returns
[`Prism`](Prism-1.md)<`P`\>
#### Defined in
[pointerToPrism.ts:21](https://github.com/theatre-js/theatre/blob/327b859ed/packages/dataverse/src/pointerToPrism.ts#L21)

View file

@ -0,0 +1,125 @@
[@theatre/dataverse](../README.md) / Prism
# Interface: Prism<V\>
Common interface for prisms.
## Type parameters
| Name |
| :------ |
| `V` |
## Table of contents
### Properties
- [isHot](Prism-1.md#ishot)
- [isPrism](Prism-1.md#isprism)
### Methods
- [getValue](Prism-1.md#getvalue)
- [keepHot](Prism-1.md#keephot)
- [onChange](Prism-1.md#onchange)
- [onStale](Prism-1.md#onstale)
## Properties
### isHot
**isHot**: `boolean`
Whether the prism is hot.
#### Defined in
[prism/Interface.ts:18](https://github.com/theatre-js/theatre/blob/327b859ed/packages/dataverse/src/prism/Interface.ts#L18)
___
### isPrism
**isPrism**: ``true``
Whether the object is a prism.
#### Defined in
[prism/Interface.ts:13](https://github.com/theatre-js/theatre/blob/327b859ed/packages/dataverse/src/prism/Interface.ts#L13)
## Methods
### getValue
**getValue**(): `V`
Gets the current value of the prism. If the value is stale, it causes the prism to freshen.
#### Returns
`V`
#### Defined in
[prism/Interface.ts:60](https://github.com/theatre-js/theatre/blob/327b859ed/packages/dataverse/src/prism/Interface.ts#L60)
___
### keepHot
**keepHot**(): `VoidFn`
Keep the prism hot, even if there are no tappers (subscribers).
#### Returns
`VoidFn`
#### Defined in
[prism/Interface.ts:34](https://github.com/theatre-js/theatre/blob/327b859ed/packages/dataverse/src/prism/Interface.ts#L34)
___
### onChange
**onChange**(`ticker`, `listener`, `immediate?`): `VoidFn`
Calls `listener` with a fresh value every time the prism _has_ a new value, throttled by Ticker.
#### Parameters
| Name | Type |
| :------ | :------ |
| `ticker` | [`Ticker`](../classes/Ticker.md) |
| `listener` | (`v`: `V`) => `void` |
| `immediate?` | `boolean` |
#### Returns
`VoidFn`
#### Defined in
[prism/Interface.ts:23](https://github.com/theatre-js/theatre/blob/327b859ed/packages/dataverse/src/prism/Interface.ts#L23)
___
### onStale
**onStale**(`cb`): `VoidFn`
#### Parameters
| Name | Type |
| :------ | :------ |
| `cb` | () => `void` |
#### Returns
`VoidFn`
#### Defined in
[prism/Interface.ts:29](https://github.com/theatre-js/theatre/blob/327b859ed/packages/dataverse/src/prism/Interface.ts#L29)

View file

@ -0,0 +1,395 @@
[@theatre/dataverse](../README.md) / prism
# Namespace: prism
Creates a prism from the passed function that adds all prisms referenced
in it as dependencies, and reruns the function when these change.
**`Param`**
The function to rerun when the prisms referenced in it change.
## Table of contents
### Variables
- [effect](prism.md#effect)
- [ensurePrism](prism.md#ensureprism)
- [inPrism](prism.md#inprism)
- [memo](prism.md#memo)
- [ref](prism.md#ref)
- [scope](prism.md#scope)
- [source](prism.md#source)
- [state](prism.md#state)
- [sub](prism.md#sub)
## Variables
### effect
**effect**: (`key`: `string`, `cb`: () => () => `void`, `deps?`: `unknown`[]) => `void`
#### Type declaration
▸ (`key`, `cb`, `deps?`): `void`
An effect hook, similar to React's `useEffect()`, but is not sensitive to call order by using `key`.
##### Parameters
| Name | Type | Description |
| :------ | :------ | :------ |
| `key` | `string` | the key for the effect. Should be uniqe inside of the prism. |
| `cb` | () => () => `void` | the callback function. Requires returning a cleanup function. |
| `deps?` | `unknown`[] | the dependency array |
##### Returns
`void`
#### Defined in
[prism/prism.ts:885](https://github.com/theatre-js/theatre/blob/327b859ed/packages/dataverse/src/prism/prism.ts#L885)
___
### ensurePrism
**ensurePrism**: () => `void`
#### Type declaration
▸ (): `void`
This is useful to make sure your code is running inside a `prism()` call.
##### Returns
`void`
**`Example`**
```ts
import {prism} from '@theatre/dataverse'
function onlyUsefulInAPrism() {
prism.ensurePrism()
}
prism(() => {
onlyUsefulInAPrism() // will run fine
})
setTimeout(() => {
onlyUsefulInAPrism() // throws an error
console.log('This will never get logged')
}, 0)
```
#### Defined in
[prism/prism.ts:887](https://github.com/theatre-js/theatre/blob/327b859ed/packages/dataverse/src/prism/prism.ts#L887)
___
### inPrism
**inPrism**: () => `boolean`
#### Type declaration
▸ (): `boolean`
##### Returns
`boolean`
true if the current function is running inside a `prism()` call.
#### Defined in
[prism/prism.ts:891](https://github.com/theatre-js/theatre/blob/327b859ed/packages/dataverse/src/prism/prism.ts#L891)
___
### memo
**memo**: <T\>(`key`: `string`, `fn`: () => `T`, `deps`: `undefined` \| `any`[] \| readonly `any`[]) => `T`
#### Type declaration
<`T`\>(`key`, `fn`, `deps`): `T`
`prism.memo()` works just like React's `useMemo()` hook. It's a way to cache the result of a function call.
The only difference is that `prism.memo()` requires a key to be passed into it, whlie `useMemo()` doesn't.
This means that we can call `prism.memo()` in any order, and we can call it multiple times with the same key.
##### Type parameters
| Name |
| :------ |
| `T` |
##### Parameters
| Name | Type | Description |
| :------ | :------ | :------ |
| `key` | `string` | The key for the memo. Should be unique inside of the prism |
| `fn` | () => `T` | The function to memoize |
| `deps` | `undefined` \| `any`[] \| readonly `any`[] | The dependency array. Provide `[]` if you want to the value to be memoized only once and never re-calculated. |
##### Returns
`T`
The result of the function call
**`Example`**
```ts
const pr = prism(() => {
const memoizedReturnValueOfExpensiveFn = prism.memo("memo1", expensiveFn, [])
})
```
#### Defined in
[prism/prism.ts:886](https://github.com/theatre-js/theatre/blob/327b859ed/packages/dataverse/src/prism/prism.ts#L886)
___
### ref
**ref**: <T\>(`key`: `string`, `initialValue`: `T`) => `IRef`<`T`\>
#### Type declaration
<`T`\>(`key`, `initialValue`): `IRef`<`T`\>
Just like React's `useRef()`, `prism.ref()` allows us to create a prism that holds a reference to some value.
The only difference is that `prism.ref()` requires a key to be passed into it, whlie `useRef()` doesn't.
This means that we can call `prism.ref()` in any order, and we can call it multiple times with the same key.
##### Type parameters
| Name |
| :------ |
| `T` |
##### Parameters
| Name | Type | Description |
| :------ | :------ | :------ |
| `key` | `string` | The key for the ref. Should be unique inside of the prism. |
| `initialValue` | `T` | The initial value for the ref. |
##### Returns
`IRef`<`T`\>
`{current: V}` - The ref object.
Note that the ref object will always return its initial value if the prism is cold. It'll only record
its current value if the prism is hot (and will forget again if the prism goes cold again).
**`Example`**
```ts
const pr = prism(() => {
const ref1 = prism.ref("ref1", 0)
console.log(ref1.current) // will print 0, and if the prism is hot, it'll print the current value
ref1.current++ // changing the current value of the ref
})
```
#### Defined in
[prism/prism.ts:884](https://github.com/theatre-js/theatre/blob/327b859ed/packages/dataverse/src/prism/prism.ts#L884)
___
### scope
**scope**: <T\>(`key`: `string`, `fn`: () => `T`) => `T`
#### Type declaration
<`T`\>(`key`, `fn`): `T`
##### Type parameters
| Name |
| :------ |
| `T` |
##### Parameters
| Name | Type |
| :------ | :------ |
| `key` | `string` |
| `fn` | () => `T` |
##### Returns
`T`
#### Defined in
[prism/prism.ts:889](https://github.com/theatre-js/theatre/blob/327b859ed/packages/dataverse/src/prism/prism.ts#L889)
___
### source
**source**: <V\>(`subscribe`: (`fn`: (`val`: `V`) => `void`) => `VoidFn`, `getValue`: () => `V`) => `V`
#### Type declaration
<`V`\>(`subscribe`, `getValue`): `V`
`prism.source()` allow a prism to react to changes in some external source (other than other prisms).
For example, `Atom.pointerToPrism()` uses `prism.source()` to create a prism that reacts to changes in the atom's value.
##### Type parameters
| Name |
| :------ |
| `V` |
##### Parameters
| Name | Type | Description |
| :------ | :------ | :------ |
| `subscribe` | (`fn`: (`val`: `V`) => `void`) => `VoidFn` | The prism will call this function as soon as the prism goes hot. This function should return an unsubscribe function function which the prism will call when it goes cold. |
| `getValue` | () => `V` | A function that returns the current value of the external source. |
##### Returns
`V`
The current value of the source
Example:
```ts
function prismFromInputElement(input: HTMLInputElement): Prism<string> {
function listen(cb: (value: string) => void) {
const listener = () => {
cb(input.value)
}
input.addEventListener('input', listener)
return () => {
input.removeEventListener('input', listener)
}
}
function get() {
return input.value
}
return prism(() => prism.source(listen, get))
}
```
#### Defined in
[prism/prism.ts:892](https://github.com/theatre-js/theatre/blob/327b859ed/packages/dataverse/src/prism/prism.ts#L892)
___
### state
**state**: <T\>(`key`: `string`, `initialValue`: `T`) => [`T`, (`val`: `T`) => `void`]
#### Type declaration
<`T`\>(`key`, `initialValue`): [`T`, (`val`: `T`) => `void`]
A state hook, similar to react's `useState()`.
##### Type parameters
| Name |
| :------ |
| `T` |
##### Parameters
| Name | Type | Description |
| :------ | :------ | :------ |
| `key` | `string` | the key for the state |
| `initialValue` | `T` | the initial value |
##### Returns
[`T`, (`val`: `T`) => `void`]
[currentState, setState]
**`Example`**
```ts
import {prism} from 'dataverse'
// This prism holds the current mouse position and updates when the mouse moves
const mousePositionD = prism(() => {
const [pos, setPos] = prism.state<[x: number, y: number]>('pos', [0, 0])
prism.effect(
'setupListeners',
() => {
const handleMouseMove = (e: MouseEvent) => {
setPos([e.screenX, e.screenY])
}
document.addEventListener('mousemove', handleMouseMove)
return () => {
document.removeEventListener('mousemove', handleMouseMove)
}
},
[],
)
return pos
})
```
#### Defined in
[prism/prism.ts:888](https://github.com/theatre-js/theatre/blob/327b859ed/packages/dataverse/src/prism/prism.ts#L888)
___
### sub
**sub**: <T\>(`key`: `string`, `fn`: () => `T`, `deps`: `undefined` \| `any`[]) => `T`
#### Type declaration
<`T`\>(`key`, `fn`, `deps`): `T`
Just an alias for `prism.memo(key, () => prism(fn), deps).getValue()`. It creates a new prism, memoizes it, and returns the value.
`prism.sub()` is useful when you want to divide your prism into smaller prisms, each of which
would _only_ recalculate when _certain_ dependencies change. In other words, it's an optimization tool.
##### Type parameters
| Name |
| :------ |
| `T` |
##### Parameters
| Name | Type | Description |
| :------ | :------ | :------ |
| `key` | `string` | The key for the memo. Should be unique inside of the prism |
| `fn` | () => `T` | The function to run inside the prism |
| `deps` | `undefined` \| `any`[] | The dependency array. Provide `[]` if you want to the value to be memoized only once and never re-calculated. |
##### Returns
`T`
The value of the inner prism
#### Defined in
[prism/prism.ts:890](https://github.com/theatre-js/theatre/blob/327b859ed/packages/dataverse/src/prism/prism.ts#L890)

View file

@ -25,16 +25,20 @@
"build:js": "node -r esbuild-register ./devEnv/build.ts", "build:js": "node -r esbuild-register ./devEnv/build.ts",
"build:api-json": "api-extractor run --local --config devEnv/api-extractor.json", "build:api-json": "api-extractor run --local --config devEnv/api-extractor.json",
"prepublish": "node ../../devEnv/ensurePublishing.js", "prepublish": "node ../../devEnv/ensurePublishing.js",
"clean": "rm -rf ./dist && rm -f tsconfig.tsbuildinfo" "clean": "rm -rf ./dist && rm -f tsconfig.tsbuildinfo",
"docs": "typedoc src/index.ts --out api --plugin typedoc-plugin-markdown --readme none",
"precommit": "yarn run docs"
}, },
"devDependencies": { "devDependencies": {
"@microsoft/api-extractor": "^7.18.11", "@microsoft/api-extractor": "^7.36.4",
"@types/jest": "^26.0.23", "@types/jest": "^26.0.23",
"@types/lodash-es": "^4.17.4", "@types/lodash-es": "^4.17.4",
"@types/node": "^15.6.2", "@types/node": "^15.6.2",
"esbuild": "^0.12.15", "esbuild": "^0.12.15",
"esbuild-register": "^2.5.0", "esbuild-register": "^2.5.0",
"npm-run-all": "^4.1.5", "npm-run-all": "^4.1.5",
"typedoc": "^0.24.8",
"typedoc-plugin-markdown": "^3.15.4",
"typescript": "5.1.6" "typescript": "5.1.6"
}, },
"dependencies": { "dependencies": {

View file

@ -28,8 +28,8 @@ describe(`The exhaustive guide to dataverse`, () => {
describe(`0 - Concepts`, () => { describe(`0 - Concepts`, () => {
// There 4 main concepts in dataverse: // There 4 main concepts in dataverse:
// - Atoms, hold the state of your application. // - Atoms, hold the state of your application.
// - Pointers are a type-safe way to get/set/react-to changes in Atoms. // - Pointers are a type-safe way to refer to specific properties of atoms.
// - Prisms are functions that react to changes in atoms and other prisms. // - Prisms are functions that derive values from atoms or from other prisms.
// - Tickers are a way to schedule and synchronise computations. // - Tickers are a way to schedule and synchronise computations.
// before we dive into the concepts, let me show you how a simple dataverse setup looks like. // before we dive into the concepts, let me show you how a simple dataverse setup looks like.

View file

@ -1,6 +1,8 @@
# @theatre/react # @theatre/react
Utilities for using [Theatre.js](https://www.theatrejs.com) or [Dataverse](https://github.com/theatre-js/theatre/tree/main/packages/dataverse) with React. Utilities for using [Theatre.js](https://www.theatrejs.com) or
[Dataverse](https://github.com/theatre-js/theatre/tree/main/packages/dataverse)
with React.
## Documentation ## Documentation
@ -42,7 +44,9 @@ Usage with Theatre.js pointers:
import {useVal} from '@theatre/react' import {useVal} from '@theatre/react'
import {getProject} from '@theatre/core' import {getProject} from '@theatre/core'
const obj = getProject('my project').sheet('my sheet').object('my object', {foo: 'default value of props.foo'}) const obj = getProject('my project')
.sheet('my sheet')
.object('my object', {foo: 'default value of props.foo'})
function Component() { function Component() {
const value = useVal(obj.props.foo) const value = useVal(obj.props.foo)
@ -50,11 +54,15 @@ function Component() {
} }
``` ```
_Note that `useVal()` is a React hook, so it can only be used inside a React component's render function. `val()` on the other hand, can be used either inside a `prism` (which would be reactive) or anywhere where reactive values are not needed._ _Note that `useVal()` is a React hook, so it can only be used inside a React
component's render function. `val()` on the other hand, can be used either
inside a `prism` (which would be reactive) or anywhere where reactive values are
not needed._
### `usePrism(fn, deps)` ### `usePrism(fn, deps)`
Creates a prism out of `fn` and subscribes the element to the value of the created prism. Creates a prism out of `fn` and subscribes the element to the value of the
created prism.
```tsx ```tsx
import {Atom, val, prism} from '@theatre/dataverse' import {Atom, val, prism} from '@theatre/dataverse'
@ -63,7 +71,8 @@ import {usePrism} from '@theatre/react'
const state = new Atom({a: 1, b: 1}) const state = new Atom({a: 1, b: 1})
function Component(props: {which: 'a' | 'b'}) { function Component(props: {which: 'a' | 'b'}) {
const value = usePrism(() => { const value = usePrism(
() => {
prism.isPrism() // true prism.isPrism() // true
// note that this function is running inside a prism, so all of prism's // note that this function is running inside a prism, so all of prism's
// hooks (prism.memo(), prism.effect(), etc) are available // hooks (prism.memo(), prism.effect(), etc) are available
@ -71,10 +80,65 @@ function Component(props: {which: 'a' | 'b'}) {
return doExpensiveComputation(num) return doExpensiveComputation(num)
}, },
// since our prism reads `props.which`, we should include it in the deps array // since our prism reads `props.which`, we should include it in the deps array
[props.which] [props.which],
) )
return <div>{value}</div> return <div>{value}</div>
} }
``` ```
> Note that just like `useMemo(..., deps)`, it's necessary to provide a `deps` array to `usePrism()`. > Note that just like `useMemo(..., deps)`, it's necessary to provide a `deps`
> array to `usePrism()`.
### `usePrismInstance(prismInstance)`
Subscribes the element to the value of the given prism instance.
```tsx
import {Atom, val, prism} from '@theatre/dataverse'
import {usePrismInstance} from '@theatre/react'
const state = new Atom({a: 1, b: 1})
const p = prism(() => {
return val(state.pointer.a) + val(state.pointer.b)
})
function Component() {
const value = usePrismInstance(p)
return <div>{value}</div>
}
```
### `useAtom(initialValue)`
/\*\* Creates a new Atom, similar to useState(), but the component won't
re-render if the value of the atom changes.
```tsx
import {useAtom, useVal} from '@theatre/react'
import {useEffect} from 'react'
function MyComponent() {
const atom = useAtom({count: 0, ready: false})
const onClick = () =>
atom.setByPointer(
(p) => p.count,
(count) => count + 1,
)
useEffect(() => {
setTimeout(() => {
atom.setByPointer((p) => p.ready, true)
}, 1000)
}, [])
const ready = useVal(atom.pointer.ready)
if (!ready) return <div>Loading...</div>
return <button onClick={onClick}>Click me</button>
}
```
## Links
- Learn more about [Dataverse](../dataverse/README.md)

View file

@ -5,18 +5,10 @@
*/ */
import type {Prism} from '@theatre/dataverse' import type {Prism} from '@theatre/dataverse'
import {Atom} from '@theatre/dataverse' import {prism, val, Atom} from '@theatre/dataverse'
import {prism, val} from '@theatre/dataverse'
import {findIndex} from 'lodash-es' import {findIndex} from 'lodash-es'
import queueMicrotask from 'queue-microtask' import queueMicrotask from 'queue-microtask'
import { import {useCallback, useLayoutEffect, useRef, useState} from 'react'
useCallback,
useEffect,
useLayoutEffect,
useMemo,
useRef,
useState,
} from 'react'
import {unstable_batchedUpdates} from 'react-dom' import {unstable_batchedUpdates} from 'react-dom'
type $IntentionalAny = any type $IntentionalAny = any
@ -374,38 +366,44 @@ export function usePrismInstance<T>(der: Prism<T>, debugLabel?: string): T {
} }
/** /**
* This makes sure the prism prism remains hot as long as the * Creates a new Atom, similar to useState(), but the component
* component calling the hook is alive, but it does not * won't re-render if the value of the atom changes.
* return the value of the prism, and it does not
* re-render the component if the value of the prism changes.
* *
* Use this hook if you plan to read a prism in a * @param initialState - Initial state
* useEffect() call, without the prism causing your * @returns The Atom
* element to re-render. *
* @example
*
* Usage
* ```tsx
* import {useAtom, useVal} from '@theatre/react'
* import {useEffect} from 'react'
*
* function MyComponent() {
* const atom = useAtom({count: 0, ready: false})
*
* const onClick = () =>
* atom.setByPointer(
* (p) => p.count,
* (count) => count + 1,
* )
*
* useEffect(() => {
* setTimeout(() => {
* atom.setByPointer((p) => p.ready, true)
* }, 1000)
* }, [])
*
* const ready = useVal(atom.pointer.ready)
* if (!ready) return <div>Loading...</div>
* return <button onClick={onClick}>Click me</button>
* }
* ```
*/ */
export function usePrismWithoutReRender<T>( export function useAtom<T>(initialState: T): Atom<T> {
fn: () => T, const ref = useRef<Atom<T>>(undefined as $IntentionalAny)
deps: unknown[], if (!ref.current) {
): Prism<T> { ref.current = new Atom(initialState)
const pr = useMemo(() => prism(fn), deps)
return usePrismInstanceWithoutReRender(pr)
} }
return ref.current
/**
* This makes sure the prism remains hot as long as the
* component calling the hook is alive, but it does not
* return the value of the prism, and it does not
* re-render the component if the value of the prism changes.
*/
export function usePrismInstanceWithoutReRender<T>(der: Prism<T>): Prism<T> {
useEffect(() => {
const untap = der.keepHot()
return () => {
untap()
}
}, [der])
return der
} }

223
yarn.lock
View file

@ -6647,6 +6647,17 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@microsoft/api-extractor-model@npm:7.27.6":
version: 7.27.6
resolution: "@microsoft/api-extractor-model@npm:7.27.6"
dependencies:
"@microsoft/tsdoc": 0.14.2
"@microsoft/tsdoc-config": ~0.16.1
"@rushstack/node-core-library": 3.59.7
checksum: 7867feaf3a0e5accfcce3a77681248a319952a266cffc644e4f8f7df1c9e1d55adb5124df901e8cca594bb3e12d361d1fcb2bffbdbb4b20fe3113928f6535975
languageName: node
linkType: hard
"@microsoft/api-extractor@npm:^7.18.11": "@microsoft/api-extractor@npm:^7.18.11":
version: 7.18.11 version: 7.18.11
resolution: "@microsoft/api-extractor@npm:7.18.11" resolution: "@microsoft/api-extractor@npm:7.18.11"
@ -6691,6 +6702,28 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@microsoft/api-extractor@npm:^7.36.4":
version: 7.36.4
resolution: "@microsoft/api-extractor@npm:7.36.4"
dependencies:
"@microsoft/api-extractor-model": 7.27.6
"@microsoft/tsdoc": 0.14.2
"@microsoft/tsdoc-config": ~0.16.1
"@rushstack/node-core-library": 3.59.7
"@rushstack/rig-package": 0.4.1
"@rushstack/ts-command-line": 4.15.2
colors: ~1.2.1
lodash: ~4.17.15
resolve: ~1.22.1
semver: ~7.5.4
source-map: ~0.6.1
typescript: ~5.0.4
bin:
api-extractor: bin/api-extractor
checksum: 92559325cf2407fa27cb9675772956511fa35005f295cdb4dc47abd7ef9c77ba61b0f684c2e952301a76dd2cfa9e398840c8f3d9117d621300e12b0ecfbf8147
languageName: node
linkType: hard
"@microsoft/tsdoc-config@npm:0.16.2": "@microsoft/tsdoc-config@npm:0.16.2":
version: 0.16.2 version: 0.16.2
resolution: "@microsoft/tsdoc-config@npm:0.16.2" resolution: "@microsoft/tsdoc-config@npm:0.16.2"
@ -7329,6 +7362,26 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@rushstack/node-core-library@npm:3.59.7":
version: 3.59.7
resolution: "@rushstack/node-core-library@npm:3.59.7"
dependencies:
colors: ~1.2.1
fs-extra: ~7.0.1
import-lazy: ~4.0.0
jju: ~1.4.0
resolve: ~1.22.1
semver: ~7.5.4
z-schema: ~5.0.2
peerDependencies:
"@types/node": "*"
peerDependenciesMeta:
"@types/node":
optional: true
checksum: 57819d62fd662a6cf3306bf7d39c11204e094a2d5c2210639c2ac5baee58c183c02023203963cd0484a5623fd9f5dea7a223df843fb52b46a18508e6118cdc19
languageName: node
linkType: hard
"@rushstack/rig-package@npm:0.3.1": "@rushstack/rig-package@npm:0.3.1":
version: 0.3.1 version: 0.3.1
resolution: "@rushstack/rig-package@npm:0.3.1" resolution: "@rushstack/rig-package@npm:0.3.1"
@ -7349,6 +7402,16 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@rushstack/rig-package@npm:0.4.1":
version: 0.4.1
resolution: "@rushstack/rig-package@npm:0.4.1"
dependencies:
resolve: ~1.22.1
strip-json-comments: ~3.1.1
checksum: 68c5ec6c446c35939fca0444fa48e5beda736e3a5816e8b44d83df6ba8b9a2caf0ceddbdc866cd8ad3b523e42877cf6ecd467bc7839e3d618a9bb1c4b3e0b5a5
languageName: node
linkType: hard
"@rushstack/ts-command-line@npm:4.12.1": "@rushstack/ts-command-line@npm:4.12.1":
version: 4.12.1 version: 4.12.1
resolution: "@rushstack/ts-command-line@npm:4.12.1" resolution: "@rushstack/ts-command-line@npm:4.12.1"
@ -7361,6 +7424,18 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@rushstack/ts-command-line@npm:4.15.2":
version: 4.15.2
resolution: "@rushstack/ts-command-line@npm:4.15.2"
dependencies:
"@types/argparse": 1.0.38
argparse: ~1.0.9
colors: ~1.2.1
string-argv: ~0.3.1
checksum: c80dcfc99630ee51c6654c58ff41f69a3bd89c38e41d9871692bc73ee3c938ced79f8b75e182e492cafb2f6ddeb0628606856af494a0259ff6fac5b248996bed
languageName: node
linkType: hard
"@rushstack/ts-command-line@npm:4.9.1": "@rushstack/ts-command-line@npm:4.9.1":
version: 4.9.1 version: 4.9.1
resolution: "@rushstack/ts-command-line@npm:4.9.1" resolution: "@rushstack/ts-command-line@npm:4.9.1"
@ -7814,7 +7889,7 @@ __metadata:
version: 0.0.0-use.local version: 0.0.0-use.local
resolution: "@theatre/dataverse@workspace:packages/dataverse" resolution: "@theatre/dataverse@workspace:packages/dataverse"
dependencies: dependencies:
"@microsoft/api-extractor": ^7.18.11 "@microsoft/api-extractor": ^7.36.4
"@types/jest": ^26.0.23 "@types/jest": ^26.0.23
"@types/lodash-es": ^4.17.4 "@types/lodash-es": ^4.17.4
"@types/node": ^15.6.2 "@types/node": ^15.6.2
@ -7822,6 +7897,8 @@ __metadata:
esbuild-register: ^2.5.0 esbuild-register: ^2.5.0
lodash-es: ^4.17.21 lodash-es: ^4.17.21
npm-run-all: ^4.1.5 npm-run-all: ^4.1.5
typedoc: ^0.24.8
typedoc-plugin-markdown: ^3.15.4
typescript: 5.1.6 typescript: 5.1.6
languageName: unknown languageName: unknown
linkType: soft linkType: soft
@ -10317,6 +10394,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"ansi-sequence-parser@npm:^1.1.0":
version: 1.1.1
resolution: "ansi-sequence-parser@npm:1.1.1"
checksum: ead5b15c596e8e85ca02951a844366c6776769dcc9fd1bd3a0db11bb21364554822c6a439877fb599e7e1ffa0b5f039f1e5501423950457f3dcb2f480c30b188
languageName: node
linkType: hard
"ansi-styles@npm:^2.2.1": "ansi-styles@npm:^2.2.1":
version: 2.2.1 version: 2.2.1
resolution: "ansi-styles@npm:2.2.1" resolution: "ansi-styles@npm:2.2.1"
@ -18384,6 +18468,24 @@ fsevents@^1.2.7:
languageName: node languageName: node
linkType: hard linkType: hard
"handlebars@npm:^4.7.7":
version: 4.7.8
resolution: "handlebars@npm:4.7.8"
dependencies:
minimist: ^1.2.5
neo-async: ^2.6.2
source-map: ^0.6.1
uglify-js: ^3.1.4
wordwrap: ^1.0.0
dependenciesMeta:
uglify-js:
optional: true
bin:
handlebars: bin/handlebars
checksum: 00e68bb5c183fd7b8b63322e6234b5ac8fbb960d712cb3f25587d559c2951d9642df83c04a1172c918c41bcfc81bfbd7a7718bbce93b893e0135fc99edea93ff
languageName: node
linkType: hard
"har-schema@npm:^2.0.0": "har-schema@npm:^2.0.0":
version: 2.0.0 version: 2.0.0
resolution: "har-schema@npm:2.0.0" resolution: "har-schema@npm:2.0.0"
@ -19585,6 +19687,15 @@ fsevents@^1.2.7:
languageName: node languageName: node
linkType: hard linkType: hard
"is-core-module@npm:^2.13.0":
version: 2.13.0
resolution: "is-core-module@npm:2.13.0"
dependencies:
has: ^1.0.3
checksum: 053ab101fb390bfeb2333360fd131387bed54e476b26860dc7f5a700bbf34a0ec4454f7c8c4d43e8a0030957e4b3db6e16d35e1890ea6fb654c833095e040355
languageName: node
linkType: hard
"is-core-module@npm:^2.2.0": "is-core-module@npm:^2.2.0":
version: 2.2.0 version: 2.2.0
resolution: "is-core-module@npm:2.2.0" resolution: "is-core-module@npm:2.2.0"
@ -22058,6 +22169,13 @@ fsevents@^1.2.7:
languageName: node languageName: node
linkType: hard linkType: hard
"jsonc-parser@npm:^3.2.0":
version: 3.2.0
resolution: "jsonc-parser@npm:3.2.0"
checksum: 946dd9a5f326b745aa326d48a7257e3f4a4b62c5e98ec8e49fa2bdd8d96cef7e6febf1399f5c7016114fd1f68a1c62c6138826d5d90bc650448e3cf0951c53c7
languageName: node
linkType: hard
"jsonfile@npm:^4.0.0": "jsonfile@npm:^4.0.0":
version: 4.0.0 version: 4.0.0
resolution: "jsonfile@npm:4.0.0" resolution: "jsonfile@npm:4.0.0"
@ -22768,6 +22886,13 @@ fsevents@^1.2.7:
languageName: node languageName: node
linkType: hard linkType: hard
"lunr@npm:^2.3.9":
version: 2.3.9
resolution: "lunr@npm:2.3.9"
checksum: 176719e24fcce7d3cf1baccce9dd5633cd8bdc1f41ebe6a180112e5ee99d80373fe2454f5d4624d437e5a8319698ca6837b9950566e15d2cae5f2a543a3db4b8
languageName: node
linkType: hard
"lz-string@npm:^1.4.4": "lz-string@npm:^1.4.4":
version: 1.4.4 version: 1.4.4
resolution: "lz-string@npm:1.4.4" resolution: "lz-string@npm:1.4.4"
@ -22914,6 +23039,15 @@ fsevents@^1.2.7:
languageName: node languageName: node
linkType: hard linkType: hard
"marked@npm:^4.3.0":
version: 4.3.0
resolution: "marked@npm:4.3.0"
bin:
marked: bin/marked.js
checksum: 0db6817893952c3ec710eb9ceafb8468bf5ae38cb0f92b7b083baa13d70b19774674be04db5b817681fa7c5c6a088f61300815e4dd75a59696f4716ad69f6260
languageName: node
linkType: hard
"matched@npm:^5.0.0": "matched@npm:^5.0.0":
version: 5.0.1 version: 5.0.1
resolution: "matched@npm:5.0.1" resolution: "matched@npm:5.0.1"
@ -23293,6 +23427,15 @@ fsevents@^1.2.7:
languageName: node languageName: node
linkType: hard linkType: hard
"minimatch@npm:^9.0.0":
version: 9.0.3
resolution: "minimatch@npm:9.0.3"
dependencies:
brace-expansion: ^2.0.1
checksum: 253487976bf485b612f16bf57463520a14f512662e592e95c571afdab1442a6a6864b6c88f248ce6fc4ff0b6de04ac7aa6c8bb51e868e99d1d65eb0658a708b5
languageName: node
linkType: hard
"minimist@npm:^1.1.1, minimist@npm:^1.1.3, minimist@npm:^1.2.0, minimist@npm:^1.2.5, minimist@npm:^1.2.6": "minimist@npm:^1.1.1, minimist@npm:^1.1.3, minimist@npm:^1.2.0, minimist@npm:^1.2.5, minimist@npm:^1.2.6":
version: 1.2.8 version: 1.2.8
resolution: "minimist@npm:1.2.8" resolution: "minimist@npm:1.2.8"
@ -28834,6 +28977,19 @@ fsevents@^1.2.7:
languageName: node languageName: node
linkType: hard linkType: hard
"resolve@npm:~1.22.1":
version: 1.22.4
resolution: "resolve@npm:1.22.4"
dependencies:
is-core-module: ^2.13.0
path-parse: ^1.0.7
supports-preserve-symlinks-flag: ^1.0.0
bin:
resolve: bin/resolve
checksum: 23f25174c2736ce24c6d918910e0d1f89b6b38fefa07a995dff864acd7863d59a7f049e691f93b4b2ee29696303390d921552b6d1b841ed4a8101f517e1d0124
languageName: node
linkType: hard
"restore-cursor@npm:^2.0.0": "restore-cursor@npm:^2.0.0":
version: 2.0.0 version: 2.0.0
resolution: "restore-cursor@npm:2.0.0" resolution: "restore-cursor@npm:2.0.0"
@ -29489,7 +29645,7 @@ fsevents@^1.2.7:
languageName: node languageName: node
linkType: hard linkType: hard
"semver@npm:7.5.4, semver@npm:^7.3.8, semver@npm:^7.5.4": "semver@npm:7.5.4, semver@npm:^7.3.8, semver@npm:^7.5.4, semver@npm:~7.5.4":
version: 7.5.4 version: 7.5.4
resolution: "semver@npm:7.5.4" resolution: "semver@npm:7.5.4"
dependencies: dependencies:
@ -29777,6 +29933,18 @@ fsevents@^1.2.7:
languageName: node languageName: node
linkType: hard linkType: hard
"shiki@npm:^0.14.1":
version: 0.14.3
resolution: "shiki@npm:0.14.3"
dependencies:
ansi-sequence-parser: ^1.1.0
jsonc-parser: ^3.2.0
vscode-oniguruma: ^1.7.0
vscode-textmate: ^8.0.0
checksum: a4dd98e3b2a5dd8be207448f111ffb9ad2ed6c530f215714d8b61cbf91ec3edbabb09109b8ec58a26678aacd24e8161d5a9bc0c1fa1b4f64b27ceb180cbd0c89
languageName: node
linkType: hard
"side-channel@npm:^1.0.4": "side-channel@npm:^1.0.4":
version: 1.0.4 version: 1.0.4
resolution: "side-channel@npm:1.0.4" resolution: "side-channel@npm:1.0.4"
@ -32180,6 +32348,33 @@ fsevents@^1.2.7:
languageName: node languageName: node
linkType: hard linkType: hard
"typedoc-plugin-markdown@npm:^3.15.4":
version: 3.15.4
resolution: "typedoc-plugin-markdown@npm:3.15.4"
dependencies:
handlebars: ^4.7.7
peerDependencies:
typedoc: ">=0.24.0"
checksum: 0ba19732713a0e0da12ae4eb3ec634e6e7ffb7237451780ae1e8c072276fc682603502086104728477ce03e1168853346046648443a718f7393d1eab0bb23e84
languageName: node
linkType: hard
"typedoc@npm:^0.24.8":
version: 0.24.8
resolution: "typedoc@npm:0.24.8"
dependencies:
lunr: ^2.3.9
marked: ^4.3.0
minimatch: ^9.0.0
shiki: ^0.14.1
peerDependencies:
typescript: 4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x
bin:
typedoc: bin/typedoc
checksum: a46a14497f789fb3594e6c3af2e45276934ac46df40b7ed15a504ee51dc7a8013a2ffb3a54fd73abca6a2b71f97d3ec9ad356fa9aa81d29743e4645a965a2ae0
languageName: node
linkType: hard
"typescript@npm:5.1.6": "typescript@npm:5.1.6":
version: 5.1.6 version: 5.1.6
resolution: "typescript@npm:5.1.6" resolution: "typescript@npm:5.1.6"
@ -32210,6 +32405,16 @@ fsevents@^1.2.7:
languageName: node languageName: node
linkType: hard linkType: hard
"typescript@npm:~5.0.4":
version: 5.0.4
resolution: "typescript@npm:5.0.4"
bin:
tsc: bin/tsc
tsserver: bin/tsserver
checksum: 82b94da3f4604a8946da585f7d6c3025fff8410779e5bde2855ab130d05e4fd08938b9e593b6ebed165bda6ad9292b230984f10952cf82f0a0ca07bbeaa08172
languageName: node
linkType: hard
"uglify-js@npm:^3.1.4": "uglify-js@npm:^3.1.4":
version: 3.15.5 version: 3.15.5
resolution: "uglify-js@npm:3.15.5" resolution: "uglify-js@npm:3.15.5"
@ -32880,6 +33085,20 @@ fsevents@^1.2.7:
languageName: node languageName: node
linkType: hard linkType: hard
"vscode-oniguruma@npm:^1.7.0":
version: 1.7.0
resolution: "vscode-oniguruma@npm:1.7.0"
checksum: 53519d91d90593e6fb080260892e87d447e9b200c4964d766772b5053f5699066539d92100f77f1302c91e8fc5d9c772fbe40fe4c90f3d411a96d5a9b1e63f42
languageName: node
linkType: hard
"vscode-textmate@npm:^8.0.0":
version: 8.0.0
resolution: "vscode-textmate@npm:8.0.0"
checksum: 127780dfea89559d70b8326df6ec344cfd701312dd7f3f591a718693812b7852c30b6715e3cfc8b3200a4e2515b4c96f0843c0eacc0a3020969b5de262c2a4bb
languageName: node
linkType: hard
"w3c-hr-time@npm:^1.0.1, w3c-hr-time@npm:^1.0.2": "w3c-hr-time@npm:^1.0.1, w3c-hr-time@npm:^1.0.2":
version: 1.0.2 version: 1.0.2
resolution: "w3c-hr-time@npm:1.0.2" resolution: "w3c-hr-time@npm:1.0.2"