Unify Derivation and Prism 11/n

`useDerivation()` => `usePrismInstance()`
This commit is contained in:
Aria Minaei 2022-12-01 14:59:03 +01:00
parent 1236900ddf
commit b2116e9a5d
10 changed files with 51 additions and 51 deletions

View file

@ -8,7 +8,7 @@ import type {$FixMe, $IntentionalAny} from './types'
import prism from './prisms/prism/prism'
/**
* Allows creating pointer-derivations where the pointer can be switched out.
* 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

View file

@ -144,7 +144,7 @@ export const getPointerParts = <_>(
* Creates a pointer to a (nested) property of an {@link Atom}.
*
* @remarks
* Pointers are used to make derivations of properties or nested properties of
* Pointers are used to make prisms of properties or nested properties of
* {@link Atom|Atoms}.
*
* Pointers also allow easy construction of new pointers pointing to nested members

View file

@ -5,15 +5,15 @@ import type {Prism} from './Interface'
import {isPrism} from './Interface'
export default function* iterateAndCountTicks<V>(
pointerOrDerivation: Prism<V> | Pointer<V>,
pointerOrPrism: Prism<V> | Pointer<V>,
): Generator<{value: V; ticks: number}, void, void> {
let d
if (isPointer(pointerOrDerivation)) {
d = pointerToPrism(pointerOrDerivation) as Prism<V>
} else if (isPrism(pointerOrDerivation)) {
d = pointerOrDerivation
if (isPointer(pointerOrPrism)) {
d = pointerToPrism(pointerOrPrism) as Prism<V>
} else if (isPrism(pointerOrPrism)) {
d = pointerOrPrism
} else {
throw new Error(`Only pointers and derivations are supported`)
throw new Error(`Only pointers and prisms are supported`)
}
let ticksCountedSinceLastYield = 0

View file

@ -6,15 +6,15 @@ import type {Prism} from './Interface'
import {isPrism} from './Interface'
export default function* iterateOver<V>(
pointerOrDerivation: Prism<V> | Pointer<V>,
pointerOrPrism: Prism<V> | Pointer<V>,
): Generator<V, void, void> {
let d
if (isPointer(pointerOrDerivation)) {
d = pointerToPrism(pointerOrDerivation) as Prism<V>
} else if (isPrism(pointerOrDerivation)) {
d = pointerOrDerivation
if (isPointer(pointerOrPrism)) {
d = pointerToPrism(pointerOrPrism) as Prism<V>
} else if (isPrism(pointerOrPrism)) {
d = pointerOrPrism
} else {
throw new Error(`Only pointers and derivations are supported`)
throw new Error(`Only pointers and prisms are supported`)
}
const ticker = new Ticker()

View file

@ -96,7 +96,7 @@ describe('prism', () => {
const prsm = prism(() => {
const n = val(a.pointer.letter)
const iterationAtTimeOfCall = iteration
sequence.push({derivationCall: iterationAtTimeOfCall})
sequence.push({prismCall: iterationAtTimeOfCall})
prism.effect(
'f',
@ -116,13 +116,13 @@ describe('prism', () => {
sequence.push({change})
})
expect(sequence).toMatchObject([{derivationCall: 0}, {effectCall: 0}])
expect(sequence).toMatchObject([{prismCall: 0}, {effectCall: 0}])
sequence.length = 0
iteration++
a.setIn(['letter'], 'b')
ticker.tick()
expect(sequence).toMatchObject([{derivationCall: 1}, {change: 'b'}])
expect(sequence).toMatchObject([{prismCall: 1}, {change: 'b'}])
sequence.length = 0
deps = [1]
@ -130,7 +130,7 @@ describe('prism', () => {
a.setIn(['letter'], 'c')
ticker.tick()
expect(sequence).toMatchObject([
{derivationCall: 2},
{prismCall: 2},
{cleanupCall: 0},
{effectCall: 2},
{change: 'c'},
@ -156,7 +156,7 @@ describe('prism', () => {
const prsm = prism(() => {
const n = val(a.pointer.letter)
const iterationAtTimeOfCall = iteration
sequence.push({derivationCall: iterationAtTimeOfCall})
sequence.push({prismCall: iterationAtTimeOfCall})
const resultOfMemo = prism.memo(
'memo',
@ -177,7 +177,7 @@ describe('prism', () => {
})
expect(sequence).toMatchObject([
{derivationCall: 0},
{prismCall: 0},
{memoCall: 0},
{resultOfMemo: 0},
])
@ -187,7 +187,7 @@ describe('prism', () => {
a.setIn(['letter'], 'b')
ticker.tick()
expect(sequence).toMatchObject([
{derivationCall: 1},
{prismCall: 1},
{resultOfMemo: 0},
{change: 'b'},
])
@ -198,7 +198,7 @@ describe('prism', () => {
a.setIn(['letter'], 'c')
ticker.tick()
expect(sequence).toMatchObject([
{derivationCall: 2},
{prismCall: 2},
{memoCall: 2},
{resultOfMemo: 2},
{change: 'c'},

View file

@ -51,7 +51,7 @@ class HotHandle<V> {
constructor(
private readonly _fn: () => V,
private readonly _prismInstance: PrismDerivation<V>,
private readonly _prismInstance: PrismInstance<V>,
) {
for (const d of this._dependencies) {
d._addDependent(this._reactToDependencyGoingStale)
@ -198,7 +198,7 @@ class HotHandle<V> {
const emptyObject = {}
class PrismDerivation<V> implements Prism<V> {
class PrismInstance<V> implements Prism<V> {
/**
* Whether the object is a prism.
*/
@ -336,8 +336,8 @@ class PrismDerivation<V> implements Prism<V> {
* Design constraints:
* - This fix should not have a perf-penalty in production. Perhaps use a global flag + `process.env.NODE_ENV !== 'production'`
* to enable it.
* - In the case of `DerivationValuelessEmitter`, we don't control when the user calls
* `getValue()` (as opposed to `DerivationEmitter` which calls `getValue()` directly).
* - In the case of `onStale()`, we don't control when the user calls
* `getValue()` (as opposed to `onChange()` which calls `getValue()` directly).
* Perhaps we can disable the check in that case.
* - Probably the best place to add this check is right here in this method plus some changes to `reportResulutionStart()`,
* which would have to be changed to let the caller know if there is an actual collector (a prism)
@ -700,7 +700,7 @@ function inPrism(): boolean {
return !!hookScopeStack.peek()
}
const possibleDerivationToValue = <P extends Prism<$IntentionalAny> | unknown>(
const possiblePrismToValue = <P extends Prism<$IntentionalAny> | unknown>(
input: P,
): P extends Prism<infer T> ? T : P => {
if (isPrism(input)) {
@ -742,7 +742,7 @@ type IPrismFn = {
* @param fn - The function to rerun when the prisms referenced in it change.
*/
const prism: IPrismFn = (fn) => {
return new PrismDerivation(fn)
return new PrismInstance(fn)
}
class ColdScope implements PrismScope {

View file

@ -65,7 +65,7 @@ export function usePrism<T>(
boxRef.current.set(fnAsCallback)
}
const pr = useMemo(
const prsm = useMemo(
() =>
prism(() => {
const fn = boxRef.current.prism.getValue()
@ -74,7 +74,7 @@ export function usePrism<T>(
[],
)
return useDerivation(pr, debugLabel)
return usePrismInstance(prsm, debugLabel)
}
export const useVal: typeof val = (p: $IntentionalAny, debugLabel?: string) => {
@ -107,7 +107,7 @@ type QueueItem<T = unknown> = {
*/
debug?: {
/**
* The `debugLabel` given to `usePrism()/useDerivation()`
* The `debugLabel` given to `usePrism()/usePrismInstance()`
*/
label?: string
/**
@ -115,8 +115,8 @@ type QueueItem<T = unknown> = {
*/
traceOfFirstTimeRender: Error
/**
* An array of the operations done on/about this useDerivation. This is helpful to trace
* why a useDerivation's update was added to the queue and why it re-rendered
* An array of the operations done on/about this usePrismInstance. This is helpful to trace
* why a usePrismInstance's update was added to the queue and why it re-rendered
*/
history: Array<
/**
@ -150,11 +150,11 @@ type QueueItem<T = unknown> = {
*/
lastValue: T
/**
* Would be set to true if the element hosting the `useDerivation()` was unmounted
* Would be set to true if the element hosting the `usePrismInstance()` was unmounted
*/
unmounted: boolean
/**
* Adds the `useDerivation` to the update queue
* Adds the `usePrismInstance` to the update queue
*/
queueUpdate: () => void
/**
@ -224,7 +224,7 @@ function queueIfNeeded() {
item.debug?.history.push(`queue: der.getValue() errored`)
}
console.error(
'A `der.getValue()` in `useDerivation(der)` threw an error. ' +
'A `der.getValue()` in `usePrismInstance(der)` threw an error. ' +
"This may be a zombie child issue, so we're gonna try to get its value again in a normal react render phase." +
'If you see the same error again, then you either have an error in your prism code, or the deps array in `usePrism(fn, deps)` is missing ' +
'a dependency and causing the prism to read stale values.',
@ -268,19 +268,19 @@ function queueIfNeeded() {
*
* # Remove cold prism reads
*
* Prior to the latest change, the first render of every `useDerivation()` resulted in a cold read of its inner prism.
* Prior to the latest change, the first render of every `usePrismInstance()` resulted in a cold read of its inner prism.
* Cold reads are predictably slow. The reason we'd run cold reads was to comply with react's rule of not running side-effects
* during render. (Turning a prism hot is _technically_ a side-effect).
*
* However, now that users are animating scenes with hundreds of objects in the same sequence, the lag started to be noticable.
*
* This commit changes `useDerivation()` so that it turns its prism hot before rendering them.
* This commit changes `usePrismInstance()` so that it turns its prism hot before rendering them.
*
* # Freshen prisms before render
*
* Previously in order to avoid the zombie child problem (https://kaihao.dev/posts/stale-props-and-zombie-children-in-redux)
* we deferred freshening the prisms to the render phase of components. This meant that if a prism's dependencies
* changed, `useDerivation()` would schedule a re-render, regardless of whether that change actually affected the prism's
* changed, `usePrismInstance()` would schedule a re-render, regardless of whether that change actually affected the prism's
* value. Here is a contrived example:
*
* ```ts
@ -288,7 +288,7 @@ function queueIfNeeded() {
* const isPositiveD = prism(() => num.prism.getValue() >= 0)
*
* const Comp = () => {
* return <div>{useDerivation(isPositiveD)}</div>
* return <div>{usePrismInstance(isPositiveD)}</div>
* }
*
* num.set(2) // would cause Comp to re-render- even though 1 is still a positive number
@ -301,9 +301,9 @@ function queueIfNeeded() {
* the mounting tree.
*
* On the off-chance that one of them still turns out to be a zombile child, `runQueue` will defer that particular
* `useDerivation()` to be read inside a normal react render phase.
* `usePrismInstance()` to be read inside a normal react render phase.
*/
export function useDerivation<T>(der: Prism<T>, debugLabel?: string): T {
export function usePrismInstance<T>(der: Prism<T>, debugLabel?: string): T {
const _forceUpdate = useForceUpdate(debugLabel)
const ref = useRef<QueueItem<T>>(undefined as $IntentionalAny)
@ -347,7 +347,7 @@ export function useDerivation<T>(der: Prism<T>, debugLabel?: string): T {
if (process.env.NODE_ENV !== 'production') {
if (der !== ref.current.der) {
console.error(
'Argument `der` in `useDerivation(der)` should not change between renders.',
'Argument `der` in `usePrismInstance(der)` should not change between renders.',
)
}
}
@ -389,7 +389,7 @@ export function usePrismWithoutReRender<T>(
): Prism<T> {
const pr = useMemo(() => prism(fn), deps)
return useDerivationWithoutReRender(pr)
return usePrismInstanceWithoutReRender(pr)
}
/**
@ -398,7 +398,7 @@ export function usePrismWithoutReRender<T>(
* return the value of the prism, and it does not
* re-render the component if the value of the prism changes.
*/
export function useDerivationWithoutReRender<T>(der: Prism<T>): Prism<T> {
export function usePrismInstanceWithoutReRender<T>(der: Prism<T>): Prism<T> {
useEffect(() => {
const untap = der.keepHot()

View file

@ -7,7 +7,7 @@ import getStudio from '@theatre/studio/getStudio'
import type Scrub from '@theatre/studio/Scrub'
import type {IContextMenuItem} from '@theatre/studio/uiComponents/simpleContextMenu/useContextMenu'
import getDeep from '@theatre/shared/utils/getDeep'
import {useDerivation} from '@theatre/react'
import {usePrismInstance} from '@theatre/react'
import type {
$IntentionalAny,
SerializablePrimitive as SerializablePrimitive,
@ -355,7 +355,7 @@ export function useEditingToolsForSimplePropInDetailsPanel<
propConfig: PropTypeConfig_AllSimples,
): EditingTools<T> {
const der = getDerivation(pointerToProp, obj, propConfig)
return useDerivation(der)
return usePrismInstance(der)
}
type Shade =

View file

@ -5,7 +5,7 @@ import {useEffect} from 'react'
import {useLogger} from './useLogger'
import {Box, prism, pointerToPrism} from '@theatre/dataverse'
import {Atom} from '@theatre/dataverse'
import {useDerivation} from '@theatre/react'
import {usePrismInstance} from '@theatre/react'
import {selectClosestHTMLAncestor} from '@theatre/studio/utils/selectClosestHTMLAncestor'
/** To mean the presence value */
@ -95,7 +95,7 @@ function createPresenceContext(options: {
}
})
}, [itemKey])
return useDerivation(focusD)
return usePrismInstance(focusD)
},
setUserHover(itemKeyOpt) {
const prev = currentUserHoverItemB.get()

View file

@ -1,6 +1,6 @@
import {isPrism, prism, val} from '@theatre/dataverse'
import type {Prism, Pointer} from '@theatre/dataverse'
import {useDerivation} from '@theatre/react'
import {usePrismInstance} from '@theatre/react'
import type {$IntentionalAny} from '@theatre/shared/utils/types'
import React, {useMemo, useRef} from 'react'
import {invariant} from './invariant'
@ -86,7 +86,7 @@ export function deriver<Props extends {}>(
)
const allD = useMemo(() => deriveAllD(observables), observableArr)
const observedPropState = useDerivation(allD)
const observedPropState = usePrismInstance(allD)
return (
observedPropState &&