Cleanup
All checks were successful
Lint / lint (push) Successful in 11s

This commit is contained in:
Julien Valverdé
2025-07-23 13:14:37 +02:00
parent 935a6f9e87
commit 3ce2a52d2b
5 changed files with 21 additions and 66 deletions

View File

@@ -7,7 +7,8 @@ import * as Memoized from "./Memoized.js"
export const TypeId: unique symbol = Symbol.for("effect-fc/Component")
export type TypeId = typeof TypeId
export interface Component<E, R, P extends {}> extends Effect.Effect<React.FC<P>, never, R>, Component.Options {
export interface Component<E, R, P extends {}>
extends Effect.Effect<React.FC<P>, never, Exclude<R, Scope.Scope>>, Component.Options {
new(_: never): {}
readonly [TypeId]: TypeId
/** @internal */
@@ -365,46 +366,6 @@ export const withOptions: {
Object.getPrototypeOf(self),
))
export const useFC: {
<E, R, P extends {}>(
self: Component<E, R, P>
): Effect.Effect<React.FC<P>, never, Exclude<R, Scope.Scope>>
} = Effect.fn("useFC")(function* <E, R, P extends {}>(
self: Component<E, R, P>
) {
const runtimeRef = React.useRef<Runtime.Runtime<Exclude<R, Scope.Scope>>>(null!)
runtimeRef.current = yield* Effect.runtime<Exclude<R, Scope.Scope>>()
return React.useCallback(function ScopeProvider(props: P) {
const scope = Runtime.runSync(runtimeRef.current)(Hook.useScope(
Array.from(
Context.omit(...nonReactiveTags)(runtimeRef.current.context).unsafeMap.values()
),
self,
))
const FC = React.useMemo(() => {
const f = self.makeFunctionComponent(runtimeRef, scope)
f.displayName = self.displayName ?? "Anonymous"
return Memoized.isMemoized(self)
? React.memo(f, self.propsAreEqual)
: f
}, [scope])
return React.createElement(FC, props)
}, [])
})
export const use: {
<E, R, P extends {}>(
self: Component<E, R, P>,
fn: (Component: React.FC<P>) => React.ReactNode,
): Effect.Effect<React.ReactNode, never, Exclude<R, Scope.Scope>>
} = Effect.fn("use")(function*(self, fn) {
return fn(yield* useFC(self))
})
export const withRuntime: {
<E, R, P extends {}>(
context: React.Context<Runtime.Runtime<R>>,
@@ -418,5 +379,5 @@ export const withRuntime: {
context: React.Context<Runtime.Runtime<R>>,
): React.FC<P> => function WithRuntime(props) {
const runtime = React.useContext(context)
return React.createElement(Runtime.runSync(runtime)(useFC(self)), props)
return React.createElement(Runtime.runSync(runtime)(self), props)
})

View File

@@ -9,8 +9,8 @@ import * as React from "react"
// Generator version
const RouteComponent = Component.make(function* AsyncRendering() {
const VMemoizedAsyncComponent = yield* MemoizedAsyncComponent
const VAsyncComponent = yield* AsyncComponent
const MemoizedAsyncComponentFC = yield* MemoizedAsyncComponent
const AsyncComponentFC = yield* AsyncComponent
const [input, setInput] = React.useState("")
return (
@@ -20,8 +20,8 @@ const RouteComponent = Component.make(function* AsyncRendering() {
onChange={e => setInput(e.target.value)}
/>
<VMemoizedAsyncComponent fallback={React.useMemo(() => <p>Loading memoized...</p>, [])} />
<VAsyncComponent />
<MemoizedAsyncComponentFC fallback={React.useMemo(() => <p>Loading memoized...</p>, [])} />
<AsyncComponentFC />
</Flex>
)
}).pipe(
@@ -51,13 +51,15 @@ const RouteComponent = Component.make(function* AsyncRendering() {
class AsyncComponent extends Component.make(function* AsyncComponent() {
const VSubComponent = yield* Component.useFC(SubComponent)
yield* Effect.sleep("500 millis")
const SubComponentFC = yield* SubComponent
yield* Effect.sleep("500 millis") // Async operation
// Cannot use React hooks after the async operation
return (
<Flex direction="column" align="stretch">
<Text>Rendered!</Text>
<VSubComponent />
<SubComponentFC />
</Flex>
)
}).pipe(

View File

@@ -8,9 +8,6 @@ import * as React from "react"
const RouteComponent = Component.make(function* RouteComponent() {
const VSubComponent = yield* Component.useFC(SubComponent)
const VMemoizedSubComponent = yield* Component.useFC(MemoizedSubComponent)
const [value, setValue] = React.useState("")
return (
@@ -20,8 +17,8 @@ const RouteComponent = Component.make(function* RouteComponent() {
onChange={e => setValue(e.target.value)}
/>
<VSubComponent />
<VMemoizedSubComponent />
{yield* Effect.map(SubComponent, FC => <FC />)}
{yield* Effect.map(MemoizedSubComponent, FC => <FC />)}
</Flex>
)
}).pipe(

View File

@@ -10,16 +10,11 @@ const TodosStateLive = TodosState.Default("todos")
export const Route = createFileRoute("/")({
component: Component.make(function* Index() {
const context = yield* Hook.useContext(TodosStateLive, { finalizerExecutionMode: "fork" })
return yield* Effect.provide(Component.use(Todos, Todos => <Todos />), context)
return yield* Todos.pipe(
Effect.map(FC => <FC />),
Effect.provide(yield* Hook.useContext(TodosStateLive, { finalizerExecutionMode: "fork" })),
)
}).pipe(
Component.withRuntime(runtime.context)
)
// component: Component.make("Index")(
// () => Hook.useContext(TodosStateLive, { finalizerExecutionMode: "fork" }),
// Effect.andThen(context => Effect.provide(Component.use(Todos, Todos => <Todos />), context)),
// ).pipe(
// Component.withRuntime(runtime.context)
// )
})

View File

@@ -14,17 +14,17 @@ export class Todos extends Component.make(function* Todos() {
Effect.addFinalizer(() => Console.log("Todos unmounted")),
))
const VTodo = yield* Component.useFC(Todo)
const TodoFC = yield* Todo
return (
<Container>
<Heading align="center">Todos</Heading>
<Flex direction="column" align="stretch" gap="2" mt="2">
<VTodo _tag="new" />
<TodoFC _tag="new" />
{Chunk.map(todos, (v, k) =>
<VTodo key={v.id} _tag="edit" index={k} />
<TodoFC key={v.id} _tag="edit" index={k} />
)}
</Flex>
</Container>