This commit is contained in:
@@ -7,7 +7,8 @@ import * as Memoized from "./Memoized.js"
|
|||||||
export const TypeId: unique symbol = Symbol.for("effect-fc/Component")
|
export const TypeId: unique symbol = Symbol.for("effect-fc/Component")
|
||||||
export type TypeId = typeof TypeId
|
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): {}
|
new(_: never): {}
|
||||||
readonly [TypeId]: TypeId
|
readonly [TypeId]: TypeId
|
||||||
/** @internal */
|
/** @internal */
|
||||||
@@ -365,46 +366,6 @@ export const withOptions: {
|
|||||||
Object.getPrototypeOf(self),
|
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: {
|
export const withRuntime: {
|
||||||
<E, R, P extends {}>(
|
<E, R, P extends {}>(
|
||||||
context: React.Context<Runtime.Runtime<R>>,
|
context: React.Context<Runtime.Runtime<R>>,
|
||||||
@@ -418,5 +379,5 @@ export const withRuntime: {
|
|||||||
context: React.Context<Runtime.Runtime<R>>,
|
context: React.Context<Runtime.Runtime<R>>,
|
||||||
): React.FC<P> => function WithRuntime(props) {
|
): React.FC<P> => function WithRuntime(props) {
|
||||||
const runtime = React.useContext(context)
|
const runtime = React.useContext(context)
|
||||||
return React.createElement(Runtime.runSync(runtime)(useFC(self)), props)
|
return React.createElement(Runtime.runSync(runtime)(self), props)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ import * as React from "react"
|
|||||||
|
|
||||||
// Generator version
|
// Generator version
|
||||||
const RouteComponent = Component.make(function* AsyncRendering() {
|
const RouteComponent = Component.make(function* AsyncRendering() {
|
||||||
const VMemoizedAsyncComponent = yield* MemoizedAsyncComponent
|
const MemoizedAsyncComponentFC = yield* MemoizedAsyncComponent
|
||||||
const VAsyncComponent = yield* AsyncComponent
|
const AsyncComponentFC = yield* AsyncComponent
|
||||||
const [input, setInput] = React.useState("")
|
const [input, setInput] = React.useState("")
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -20,8 +20,8 @@ const RouteComponent = Component.make(function* AsyncRendering() {
|
|||||||
onChange={e => setInput(e.target.value)}
|
onChange={e => setInput(e.target.value)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<VMemoizedAsyncComponent fallback={React.useMemo(() => <p>Loading memoized...</p>, [])} />
|
<MemoizedAsyncComponentFC fallback={React.useMemo(() => <p>Loading memoized...</p>, [])} />
|
||||||
<VAsyncComponent />
|
<AsyncComponentFC />
|
||||||
</Flex>
|
</Flex>
|
||||||
)
|
)
|
||||||
}).pipe(
|
}).pipe(
|
||||||
@@ -51,13 +51,15 @@ const RouteComponent = Component.make(function* AsyncRendering() {
|
|||||||
|
|
||||||
|
|
||||||
class AsyncComponent extends Component.make(function* AsyncComponent() {
|
class AsyncComponent extends Component.make(function* AsyncComponent() {
|
||||||
const VSubComponent = yield* Component.useFC(SubComponent)
|
const SubComponentFC = yield* SubComponent
|
||||||
yield* Effect.sleep("500 millis")
|
|
||||||
|
yield* Effect.sleep("500 millis") // Async operation
|
||||||
|
// Cannot use React hooks after the async operation
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex direction="column" align="stretch">
|
<Flex direction="column" align="stretch">
|
||||||
<Text>Rendered!</Text>
|
<Text>Rendered!</Text>
|
||||||
<VSubComponent />
|
<SubComponentFC />
|
||||||
</Flex>
|
</Flex>
|
||||||
)
|
)
|
||||||
}).pipe(
|
}).pipe(
|
||||||
|
|||||||
@@ -8,9 +8,6 @@ import * as React from "react"
|
|||||||
|
|
||||||
|
|
||||||
const RouteComponent = Component.make(function* RouteComponent() {
|
const RouteComponent = Component.make(function* RouteComponent() {
|
||||||
const VSubComponent = yield* Component.useFC(SubComponent)
|
|
||||||
const VMemoizedSubComponent = yield* Component.useFC(MemoizedSubComponent)
|
|
||||||
|
|
||||||
const [value, setValue] = React.useState("")
|
const [value, setValue] = React.useState("")
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -20,8 +17,8 @@ const RouteComponent = Component.make(function* RouteComponent() {
|
|||||||
onChange={e => setValue(e.target.value)}
|
onChange={e => setValue(e.target.value)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<VSubComponent />
|
{yield* Effect.map(SubComponent, FC => <FC />)}
|
||||||
<VMemoizedSubComponent />
|
{yield* Effect.map(MemoizedSubComponent, FC => <FC />)}
|
||||||
</Flex>
|
</Flex>
|
||||||
)
|
)
|
||||||
}).pipe(
|
}).pipe(
|
||||||
|
|||||||
@@ -10,16 +10,11 @@ const TodosStateLive = TodosState.Default("todos")
|
|||||||
|
|
||||||
export const Route = createFileRoute("/")({
|
export const Route = createFileRoute("/")({
|
||||||
component: Component.make(function* Index() {
|
component: Component.make(function* Index() {
|
||||||
const context = yield* Hook.useContext(TodosStateLive, { finalizerExecutionMode: "fork" })
|
return yield* Todos.pipe(
|
||||||
return yield* Effect.provide(Component.use(Todos, Todos => <Todos />), context)
|
Effect.map(FC => <FC />),
|
||||||
|
Effect.provide(yield* Hook.useContext(TodosStateLive, { finalizerExecutionMode: "fork" })),
|
||||||
|
)
|
||||||
}).pipe(
|
}).pipe(
|
||||||
Component.withRuntime(runtime.context)
|
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)
|
|
||||||
// )
|
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -14,17 +14,17 @@ export class Todos extends Component.make(function* Todos() {
|
|||||||
Effect.addFinalizer(() => Console.log("Todos unmounted")),
|
Effect.addFinalizer(() => Console.log("Todos unmounted")),
|
||||||
))
|
))
|
||||||
|
|
||||||
const VTodo = yield* Component.useFC(Todo)
|
const TodoFC = yield* Todo
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
<Heading align="center">Todos</Heading>
|
<Heading align="center">Todos</Heading>
|
||||||
|
|
||||||
<Flex direction="column" align="stretch" gap="2" mt="2">
|
<Flex direction="column" align="stretch" gap="2" mt="2">
|
||||||
<VTodo _tag="new" />
|
<TodoFC _tag="new" />
|
||||||
|
|
||||||
{Chunk.map(todos, (v, k) =>
|
{Chunk.map(todos, (v, k) =>
|
||||||
<VTodo key={v.id} _tag="edit" index={k} />
|
<TodoFC key={v.id} _tag="edit" index={k} />
|
||||||
)}
|
)}
|
||||||
</Flex>
|
</Flex>
|
||||||
</Container>
|
</Container>
|
||||||
|
|||||||
Reference in New Issue
Block a user