Update comments
Some checks failed
Lint / lint (push) Failing after 1m4s

This commit is contained in:
Julien Valverdé
2026-02-27 10:16:52 +01:00
parent 6c14495693
commit 2c78b17f52

View File

@@ -365,7 +365,7 @@ export declare namespace make {
* - **Chained transformation functions**: Enables Effect.fn-style pipelines for composable transformations * - **Chained transformation functions**: Enables Effect.fn-style pipelines for composable transformations
* - **Automatic tracing**: Optional tracing span creation with automatic `displayName` assignment * - **Automatic tracing**: Optional tracing span creation with automatic `displayName` assignment
* *
* When a `spanName` string is provided as the first argument, the following occurs automatically: * When a `spanName` string is provided, the following occurs automatically:
* 1. A distributed tracing span is created with the specified name * 1. A distributed tracing span is created with the specified name
* 2. The resulting React component receives `displayName = spanName` for DevTools visibility * 2. The resulting React component receives `displayName = spanName` for DevTools visibility
* *
@@ -382,14 +382,23 @@ export declare namespace make {
* class MyComponent extends Component.make("MyComponent")(function* (props: { count: number }) { * class MyComponent extends Component.make("MyComponent")(function* (props: { count: number }) {
* const value = yield* someEffect * const value = yield* someEffect
* return <div>{value}</div> * return <div>{value}</div>
* }) * }) {}
* ``` * ```
* *
* @example Without name * @example Without name
* ```tsx * ```tsx
* const MyComponent = Component.make((props: { count: number }) => { * class MyComponent extends Component.make(function* (props: { count: number }) {
* return Effect.sync(() => <div>{props.count}</div>) * const value = yield* someEffect
* }) * return <div>{value}</div>
* }) {}
* ```
*
* @example Using pipeline
* ```tsx
* class MyComponent extends Component.make("MyComponent")(
* (props: { count: number }) => someEffect,
* Effect.map(value => <div>{value}</div>),
* ) {}
* ``` * ```
*/ */
export const make: ( export const make: (
@@ -431,22 +440,39 @@ export const make: (
* - To reduce tracing overhead in deeply nested component hierarchies * - To reduce tracing overhead in deeply nested component hierarchies
* - To avoid span noise in performance-sensitive applications * - To avoid span noise in performance-sensitive applications
* *
* When a string is provided as the first argument, it is used **exclusively** as the React component's * When a `spanName` string is provided, it is used **exclusively** as the React component's
* `displayName` for DevTools identification. No tracing span is created. * `displayName` for DevTools identification. No tracing span is created.
* *
* @example * @example
* ```tsx * ```tsx
* const MyComponent = Component.makeUntraced(function* (props: { count: number }) { * const MyComponent = Component.makeUntraced("MyComponent")(function* (props: { count: number }) {
* const value = yield* someEffect * const value = yield* someEffect
* return <div>{value}</div> * return <div>{value}</div>
* }) * })
* ``` * ```
* *
* @example With displayName only * @example As an opaque type using class syntax
* ```tsx * ```tsx
* const MyComponent = Component.makeUntraced("MyComponent", (props: { count: number }) => { * class MyComponent extends Component.makeUntraced("MyComponent")(function* (props: { count: number }) {
* return Effect.sync(() => <div>{props.count}</div>) * const value = yield* someEffect
* }) * return <div>{value}</div>
* }) {}
* ```
*
* @example Without name
* ```tsx
* class MyComponent extends Component.makeUntraced(function* (props: { count: number }) {
* const value = yield* someEffect
* return <div>{value}</div>
* }) {}
* ```
*
* @example Using pipeline
* ```tsx
* class MyComponent extends Component.makeUntraced("MyComponent")(
* (props: { count: number }) => someEffect,
* Effect.map(value => <div>{value}</div>),
* ) {}
* ``` * ```
*/ */
export const makeUntraced: ( export const makeUntraced: (
@@ -478,7 +504,6 @@ export const makeUntraced: (
* *
* @example * @example
* ```tsx * ```tsx
* const MyComponent = Component.make(...)
* const MyComponentWithCustomOptions = MyComponent.pipe( * const MyComponentWithCustomOptions = MyComponent.pipe(
* Component.withOptions({ * Component.withOptions({
* finalizerExecutionStrategy: ExecutionStrategy.parallel, * finalizerExecutionStrategy: ExecutionStrategy.parallel,
@@ -507,17 +532,14 @@ export const withOptions: {
* Wraps an Effect-FC Component and converts it into a standard React function component, * Wraps an Effect-FC Component and converts it into a standard React function component,
* serving as an **entrypoint** into an Effect-FC component hierarchy. * serving as an **entrypoint** into an Effect-FC component hierarchy.
* *
* This is the recommended approach for integrating Effect-FC components with the broader React ecosystem, * This is how Effect-FC components are integrated with the broader React ecosystem,
* particularly when: * particularly when:
* - Using client-side routers (TanStack Router, React Router, etc.) * - Using client-side routers (TanStack Router, React Router, etc.)
* - Implementing lazy-loaded or code-split routes * - Implementing lazy-loaded or code-split routes
* - Connecting to third-party libraries expecting standard React components * - Connecting to third-party libraries expecting standard React components
* - Creating component boundaries between Effect-FC and non-Effect-FC code * - Creating component boundaries between Effect-FC and non-Effect-FC code
* *
* The Effect runtime is obtained from the provided React Context, enabling: * The Effect runtime is obtained from the provided React Context.
* - Single-point dependency injection at the application root
* - Consistent runtime environment across entire route trees or feature modules
* - Proper resource lifecycle management across component hierarchies
* *
* @param self - The Effect-FC Component to be rendered as a standard React component * @param self - The Effect-FC Component to be rendered as a standard React component
* @param context - React Context providing the Effect Runtime for this component tree. * @param context - React Context providing the Effect Runtime for this component tree.
@@ -669,8 +691,8 @@ export const useScope = Effect.fnUntraced(function*(
* *
* @example * @example
* ```tsx * ```tsx
* const MyComponent = Component.make(function* (props) { * const MyComponent = Component.make(function*() {
* const initialData = yield* useOnMount(() => fetchData) * const initialData = yield* Component.useOnMount(() => getData)
* return <div>{initialData}</div> * return <div>{initialData}</div>
* }) * })
* ``` * ```
@@ -707,8 +729,8 @@ export declare namespace useOnChange {
* @example * @example
* ```tsx * ```tsx
* const MyComponent = Component.make(function* (props: { userId: string }) { * const MyComponent = Component.make(function* (props: { userId: string }) {
* const userData = yield* useOnChange( * const userData = yield* Component.useOnChange(
* fetchUser(props.userId), * getUser(props.userId),
* [props.userId], * [props.userId],
* ) * )
* return <div>{userData.name}</div> * return <div>{userData.name}</div>
@@ -740,7 +762,7 @@ export declare namespace useReactEffect {
* Effect hook that provides Effect-based semantics for React.useEffect. * Effect hook that provides Effect-based semantics for React.useEffect.
* *
* This hook bridges React's useEffect with the Effect system, allowing you to use Effects * This hook bridges React's useEffect with the Effect system, allowing you to use Effects
* for side effects while maintaining React's dependency tracking and lifecycle semantics. * for React side effects while maintaining React's dependency tracking and lifecycle semantics.
* *
* Unlike React.useEffect which uses imperative cleanup functions, this hook leverages the * Unlike React.useEffect which uses imperative cleanup functions, this hook leverages the
* Effect Scope API for resource management. Cleanup logic is expressed declaratively through * Effect Scope API for resource management. Cleanup logic is expressed declaratively through
@@ -756,14 +778,15 @@ export declare namespace useReactEffect {
* @example * @example
* ```tsx * ```tsx
* const MyComponent = Component.make(function* (props: { id: string }) { * const MyComponent = Component.make(function* (props: { id: string }) {
* yield* useReactEffect( * yield* Component.useReactEffect(
* () => { * () => getNotificationStreamForUser(props.id).pipe(
* const subscription = subscribe(props.id) * Stream.unwrap,
* return Effect.addFinalizer(() => subscription.unsubscribe()) * Stream.runForEach(notification => Console.log(`Notification received: ${ notification }`),
* }, * Effect.forkScoped,
* [props.id] * ),
* [props.id],
* ) * )
* return <div>Subscribed to {props.id}</div> * return <div>Subscribed to notifications for {props.id}</div>
* }) * })
* ``` * ```
*/ */
@@ -826,17 +849,17 @@ export declare namespace useReactLayoutEffect {
* *
* @example * @example
* ```tsx * ```tsx
* const MyComponent = Component.make(function* (props) { * const MyComponent = Component.make(function*() {
* const ref = React.useRef<HTMLDivElement>(null) * const ref = React.useRef<HTMLDivElement>(null)
* yield* useReactLayoutEffect( * yield* Component.useReactLayoutEffect(
* () => { * () => Effect.gen(function* () {
* if (ref.current) { * const element = ref.current
* const height = ref.current.offsetHeight * if (element) {
* // Perform layout-dependent operations * const rect = element.getBoundingClientRect()
* yield* Console.log(`Element dimensions: ${ rect.width }x${ rect.height }`)
* } * }
* return Effect.void * }),
* }, * [],
* []
* ) * )
* return <div ref={ref}>Content</div> * return <div ref={ref}>Content</div>
* }) * })
@@ -863,13 +886,11 @@ export const useReactLayoutEffect = Effect.fnUntraced(function* <E, R>(
* *
* @example * @example
* ```tsx * ```tsx
* const MyComponent = Component.make(function* (props) { * const MyComponent = Component.make(function*() {
* const runSync = yield* useRunSync() * const runSync = yield* Component.useRunSync<SomeService>() // Specify required services
* const handleClick = () => { * const runSync = yield* Component.useRunSync() // Or no service requirements
* const result = runSync(someEffect) *
* console.log(result) * return <button onClick={() => runSync(someEffect)}>Click me</button>
* }
* return <button onClick={handleClick}>Click me</button>
* }) * })
* ``` * ```
*/ */
@@ -890,17 +911,11 @@ export const useRunSync = <R = never>(): Effect.Effect<
* *
* @example * @example
* ```tsx * ```tsx
* const MyComponent = Component.make(function* (props) { * const MyComponent = Component.make(function*() {
* const runPromise = yield* useRunPromise() * const runPromise = yield* Component.useRunPromise<SomeService>() // Specify required services
* const handleClick = async () => { * const runPromise = yield* Component.useRunPromise() // Or no service requirements
* try { *
* const result = await runPromise(someEffect) * return <button onClick={() => runPromise(someEffect)}>Click me</button>
* console.log(result)
* } catch (error) {
* console.error(error)
* }
* }
* return <button onClick={handleClick}>Click me</button>
* }) * })
* ``` * ```
*/ */
@@ -929,10 +944,11 @@ export const useRunPromise = <R = never>(): Effect.Effect<
* @example * @example
* ```tsx * ```tsx
* const MyComponent = Component.make(function* (props: { onSave: (data: Data) => void }) { * const MyComponent = Component.make(function* (props: { onSave: (data: Data) => void }) {
* const handleSave = yield* useCallbackSync( * const handleSave = yield* Component.useCallbackSync(
* (data: Data) => saveData(data), * (data: Data) => Effect.sync(() => props.onSave(data)),
* [props.onSave] * [props.onSave],
* ) * )
*
* return <button onClick={() => handleSave(myData)}>Save</button> * return <button onClick={() => handleSave(myData)}>Save</button>
* }) * })
* ``` * ```
@@ -968,21 +984,12 @@ export const useCallbackSync = Effect.fnUntraced(function* <Args extends unknown
* @example * @example
* ```tsx * ```tsx
* const MyComponent = Component.make(function* (props: { onSave: (data: Data) => void }) { * const MyComponent = Component.make(function* (props: { onSave: (data: Data) => void }) {
* const handleSave = yield* useCallbackPromise( * const handleSave = yield* Component.useCallbackPromise(
* (data: Data) => saveData(data), * (data: Data) => Effect.promise(() => props.onSave(data)),
* [props.onSave] * [props.onSave],
* )
* return (
* <button onClick={async () => {
* try {
* await handleSave(myData)
* } catch (error) {
* console.error(error)
* }
* }}>
* Save
* </button>
* ) * )
*
* return <button onClick={() => handleSave(myData)}>Save</button>
* }) * })
* ``` * ```
*/ */
@@ -1024,32 +1031,49 @@ export declare namespace useContext {
* *
* @example * @example
* ```tsx * ```tsx
* const MyComponent = Component.make(function* (props) { * const MyLayer = Layer.succeed(MyService, new MyServiceImpl())
* const context = yield* useContext( * const MyComponent = Component.make(function*() {
* Layer.succeed(MyService, new MyServiceImpl()) * const context = yield* Component.useContext(MyLayer)
* const Sub = yield* SubComponent.use.pipe(
* Effect.provide(context)
* ) * )
* // Use context to access services *
* return <div>Using services</div> * return <Sub />
* })
* ```
*
* @example With memoized layer
* ```tsx
* const MyComponent = Component.make(function*(props: { id: string })) {
* const context = yield* Component.useContext(
* React.useMemo(() => Layer.succeed(MyService, new MyServiceImpl(props.id)), [props.id])
* )
* const Sub = yield* SubComponent.use.pipe(
* Effect.provide(context)
* )
*
* return <Sub />
* }) * })
* ``` * ```
* *
* @example With async layer * @example With async layer
* ```tsx * ```tsx
* const MyComponent = Component.make(function* (props) { * const MyAsyncLayer = Layer.effect(MyService, someAsyncEffect)
* const context = yield* useContext( * const MyComponent = Component.make(function*() {
* Layer.effect(MyService, () => fetchServiceConfig()) * const context = yield* Component.useContext(MyAsyncLayer)
* const Sub = yield* SubComponent.use.pipe(
* Effect.provide(context)
* ) * )
* return <div>Using async services</div>
* })
* *
* // Must be wrapped with Async.async * return <Sub />
* export default Async.async(MyComponent) * }).pipe(
* ``` * Async.async // Required to handle async layer effects
* )
*/ */
export const useContext = <ROut, E, RIn>( export const useContext = <ROut, E, RIn>(
layer: Layer.Layer<ROut, E, RIn>, layer: Layer.Layer<ROut, E, RIn>,
options?: useContext.Options, options?: useContext.Options,
): Effect.Effect<Context.Context<ROut>, E, Exclude<RIn, Scope.Scope>> => useOnChange(() => Effect.context<RIn>().pipe( ): Effect.Effect<Context.Context<ROut>, E, RIn | Scope.Scope> => useOnChange(() => Effect.context<RIn>().pipe(
Effect.map(context => ManagedRuntime.make(Layer.provide(layer, Layer.succeedContext(context)))), Effect.map(context => ManagedRuntime.make(Layer.provide(layer, Layer.succeedContext(context)))),
Effect.tap(runtime => Effect.addFinalizer(() => runtime.disposeEffect)), Effect.tap(runtime => Effect.addFinalizer(() => runtime.disposeEffect)),
Effect.andThen(runtime => runtime.runtimeEffect), Effect.andThen(runtime => runtime.runtimeEffect),