@@ -2,6 +2,7 @@
|
||||
/** biome-ignore-all lint/complexity/useArrowFunction: necessary for class prototypes */
|
||||
import { Context, type Duration, Effect, Equivalence, Exit, Function, identity, Layer, Pipeable, Predicate, References, Scheduler, Scope, Tracer } from "effect"
|
||||
import * as React from "react"
|
||||
import * as Refreshable from "./Refreshable.js"
|
||||
import * as ScopeRegistry from "./ScopeRegistry.js"
|
||||
|
||||
|
||||
@@ -48,6 +49,7 @@ export interface ComponentImplPrototype<R, F extends Component.Signature> {
|
||||
readonly use: Effect.Effect<F, never, Exclude<R, Scope.Scope>>
|
||||
|
||||
asFunctionComponent(contextRef: React.Ref<Context.Context<Exclude<R, Scope.Scope>>>): F
|
||||
asRefreshableFunctionComponent(contextRef: React.Ref<Context.Context<Exclude<R, Scope.Scope>>>): F
|
||||
setFunctionComponentName(f: F): void
|
||||
transformFunctionComponent(f: F): F
|
||||
}
|
||||
@@ -67,6 +69,43 @@ export const ComponentImplPrototype: ComponentImplPrototype<any, any> = Object.f
|
||||
)
|
||||
},
|
||||
|
||||
asRefreshableFunctionComponent<P extends {}, A extends React.ReactNode, E, R, F extends Component.Signature>(
|
||||
this: ComponentImpl<P, A, E, R, F>,
|
||||
contextRef: React.RefObject<Context.Context<Exclude<R, Scope.Scope>>>,
|
||||
) {
|
||||
if (!Refreshable.isRefreshable(this))
|
||||
return this.asFunctionComponent(contextRef)
|
||||
|
||||
const cell = this[Refreshable.RefreshableTypeId]
|
||||
|
||||
let current = cell.current
|
||||
let functionComponent = current.asFunctionComponent(contextRef)
|
||||
|
||||
// Calling the current renderer inside this stable component deliberately
|
||||
// keeps its hooks on the same fiber until resetRevision changes.
|
||||
const Implementation = (props: P) => {
|
||||
if (current !== cell.current) {
|
||||
current = cell.current
|
||||
functionComponent = current.asFunctionComponent(contextRef)
|
||||
}
|
||||
return functionComponent(props)
|
||||
}
|
||||
|
||||
const RefreshableComponent = (props: P) => {
|
||||
const snapshot = React.useSyncExternalStore(
|
||||
cell.subscribe,
|
||||
cell.getSnapshot,
|
||||
cell.getSnapshot,
|
||||
)
|
||||
return React.createElement(Implementation, {
|
||||
...props,
|
||||
key: snapshot.resetRevision,
|
||||
})
|
||||
}
|
||||
|
||||
return RefreshableComponent as F
|
||||
},
|
||||
|
||||
setFunctionComponentName<P extends {}, A extends React.ReactNode, E, R, F extends Component.Signature>(
|
||||
this: ComponentImpl<P, A, E, R, F>,
|
||||
f: React.FC<P>,
|
||||
@@ -98,7 +137,7 @@ const use = Effect.fnUntraced(function* <P extends {}, A extends React.ReactNode
|
||||
!Equivalence.Array(Equivalence.strictEqual())(services, previousServicesRef.current)
|
||||
) {
|
||||
previousServicesRef.current = services
|
||||
const f = self.asFunctionComponent(contextRef)
|
||||
const f = self.asRefreshableFunctionComponent(contextRef)
|
||||
self.setFunctionComponentName(f)
|
||||
componentRef.current = self.transformFunctionComponent(f)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user