Unify Derivation and Prism 11/n
`useDerivation()` => `usePrismInstance()`
This commit is contained in:
parent
1236900ddf
commit
b2116e9a5d
10 changed files with 51 additions and 51 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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'},
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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 =
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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 &&
|
||||
|
|
Loading…
Reference in a new issue