Unify Derivation and Prism 7/n

This commit is contained in:
Aria Minaei 2022-12-01 14:41:46 +01:00
parent 859cb40e0f
commit acf34d393d
25 changed files with 134 additions and 141 deletions

View file

@ -65,16 +65,16 @@ export function usePrism<T>(
boxRef.current.set(fnAsCallback)
}
const derivation = useMemo(
const pr = useMemo(
() =>
prism(() => {
const fn = boxRef.current.derivation.getValue()
const fn = boxRef.current.prism.getValue()
return fn()
}),
[],
)
return useDerivation(derivation, debugLabel)
return useDerivation(pr, debugLabel)
}
export const useVal: typeof val = (p: $IntentionalAny, debugLabel?: string) => {
@ -88,9 +88,9 @@ export const useVal: typeof val = (p: $IntentionalAny, debugLabel?: string) => {
let lastOrder = 0
/**
* A sorted array of derivations that need to be refreshed. The derivations are sorted
* by their order, which means a parent derivation always gets priority to children
* and descendents. Ie. we refresh the derivations top to bottom.
* A sorted array of prisms that need to be refreshed. The prisms are sorted
* by their order, which means a parent prism always gets priority to children
* and descendents. Ie. we refresh the prisms top to bottom.
*/
const queue: QueueItem[] = []
const setOfQueuedItems = new Set<QueueItem>()
@ -99,7 +99,7 @@ type QueueItem<T = unknown> = {
order: number
/**
* runUpdate() is the equivalent of a forceUpdate() call. It would only be called
* if the value of the inner derivation has _actually_ changed.
* if the value of the inner prism has _actually_ changed.
*/
runUpdate: VoidFn
/**
@ -142,11 +142,11 @@ type QueueItem<T = unknown> = {
>
}
/**
* A reference to the derivation
* A reference to the prism
*/
der: Prism<T>
/**
* The last value of this derivation.
* The last value of this prism.
*/
lastValue: T
/**
@ -246,17 +246,17 @@ function queueIfNeeded() {
})
}
/**
* A React hook that returns the value of the derivation that it received as the first argument.
* A React hook that returns the value of the prism that it received as the first argument.
* It works like an implementation of Dataverse's Ticker, except that it runs the side effects in
* an order where a component's derivation is guaranteed to run before any of its descendents' derivations.
* an order where a component's prism is guaranteed to run before any of its descendents' prisms.
*
* @param der - The derivation
* @param der - The prism
* @param debugLabel - The label used by the debugger
*
* @remarks
* It looks like this new implementation of useDerivation() manages to:
* 1. Not over-calculate the derivations
* 2. Render derivation in ancestor -\> descendent order
* It looks like this new implementation of usePrism() manages to:
* 1. Not over-calculate the prisms
* 2. Render prism in ancestor -\> descendent order
* 3. Not set off React's concurrent mode alarms
*
*
@ -266,21 +266,21 @@ function queueIfNeeded() {
*
* Notes on the latest implementation:
*
* # Remove cold derivation reads
* # Remove cold prism reads
*
* Prior to the latest change, the first render of every `useDerivation()` resulted in a cold read of its inner derivation.
* Prior to the latest change, the first render of every `useDerivation()` 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 derivation hot is _technically_ a side-effect).
* 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 derivation hot before rendering them.
* This commit changes `useDerivation()` so that it turns its prism hot before rendering them.
*
* # Freshen derivations before render
* # 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 derivations to the render phase of components. This meant that if a derivation's dependencies
* changed, `useDerivation()` would schedule a re-render, regardless of whether that change actually affected the derivation's
* 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
* value. Here is a contrived example:
*
* ```ts
@ -294,10 +294,10 @@ function queueIfNeeded() {
* num.set(2) // would cause Comp to re-render- even though 1 is still a positive number
* ```
*
* We now avoid this problem by freshening the derivation (i.e. calling `der.getValue()`) inside `runQueue()`,
* and then only causing a re-render if the derivation's value is actually changed.
* We now avoid this problem by freshening the prism (i.e. calling `der.getValue()`) inside `runQueue()`,
* and then only causing a re-render if the prism's value is actually changed.
*
* This still avoids the zombie-child problem because `runQueue` reads the derivations in-order of their position in
* This still avoids the zombie-child problem because `runQueue` reads the prisms in-order of their position in
* the mounting tree.
*
* On the off-chance that one of them still turns out to be a zombile child, `runQueue` will defer that particular
@ -374,29 +374,29 @@ export function useDerivation<T>(der: Prism<T>, debugLabel?: string): T {
}
/**
* This makes sure the prism derivation remains hot as long as the
* This makes sure the prism prism remains hot as long as the
* component calling the hook is alive, but it does not
* return the value of the derivation, and it does not
* re-render the component if the value of the derivation 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 derivation in a
* useEffect() call, without the derivation causing your
* Use this hook if you plan to read a prism in a
* useEffect() call, without the prism causing your
* element to re-render.
*/
export function usePrismWithoutReRender<T>(
fn: () => T,
deps: unknown[],
): Prism<T> {
const derivation = useMemo(() => prism(fn), deps)
const pr = useMemo(() => prism(fn), deps)
return useDerivationWithoutReRender(derivation)
return useDerivationWithoutReRender(pr)
}
/**
* This makes sure the derivation remains hot as long as the
* 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 derivation, and it does not
* re-render the component if the value of the derivation changes.
* 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> {
useEffect(() => {