Change useContextFromLayer to useLayer
Lint / lint (push) Failing after 43s

This commit is contained in:
Julien Valverdé
2026-07-20 20:40:13 +02:00
parent 19d5aba009
commit 9a93d72e48
13 changed files with 25 additions and 34 deletions
+4 -4
View File
@@ -370,7 +370,7 @@ that behavior, pass their tags with `Component.withOptions({ nonReactiveTags:
## Provide Services To A Subtree
Use `Component.useContextFromLayer` when an Effect View component should provide
Use `Component.useLayer` when an Effect View component should provide
extra services to another Effect View component. It turns a `Layer` into a runtime
context that can be provided to `.use`.
@@ -381,19 +381,19 @@ import { GreetingView } from "./Greeting"
import { GreetingLive } from "./services"
export const GreetingPageView = Component.make("GreetingPage")(function* () {
const context = yield* Component.useContextFromLayer(GreetingLive)
const context = yield* Component.useLayer(GreetingLive)
const Greeting = yield* GreetingView.use.pipe(Effect.provide(context))
return <Greeting name="Effect" />
})
```
The layer passed to `useContextFromLayer` should be stable. If a new layer value
The layer passed to `useLayer` should be stable. If a new layer value
is created on every render, Effect View has to build a new context, which can
cause unnecessary re-renders and scoped lifecycle restarts. Define static layers
outside the component, or memoize layers that depend on React props or state.
Layers built with `useContextFromLayer` are scoped to the component that builds
Layers built with `useLayer` are scoped to the component that builds
them. That means service construction can register scoped logic, acquire
resources, fork scoped fibers, and add finalizers that are tied to that
component's lifecycle.
+1 -1
View File
@@ -42,7 +42,7 @@ export class TodosView extends Component.make("TodosView")(function*() {
}) {}
const Index = Component.make("IndexView")(function*() {
const context = yield* Component.useContextFromLayer(TodosState.Default)
const context = yield* Component.useLayer(TodosState.Default)
const Todos = yield* Effect.provide(TodosView.use, context)
return <Todos />
+5 -7
View File
@@ -1099,7 +1099,7 @@ export declare namespace useContext {
* ```tsx
* const MyLayer = Layer.succeed(MyService, new MyServiceImpl())
* const MyComponent = Component.make(function*() {
* const context = yield* Component.useContextFromLayer(MyLayer)
* const context = yield* Component.useLayer(MyLayer)
* const Sub = yield* SubComponent.use.pipe(
* Effect.provide(context)
* )
@@ -1111,7 +1111,7 @@ export declare namespace useContext {
* @example With memoized layer
* ```tsx
* const MyComponent = Component.make(function*(props: { id: string })) {
* const context = yield* Component.useContextFromLayer(
* const context = yield* Component.useLayer(
* React.useMemo(() => Layer.succeed(MyService, new MyServiceImpl(props.id)), [props.id])
* )
* const Sub = yield* SubComponent.use.pipe(
@@ -1126,17 +1126,15 @@ export declare namespace useContext {
* ```tsx
* const MyAsyncLayer = Layer.effect(MyService, someAsyncEffect)
* const MyComponent = Component.make(function*() {
* const context = yield* Component.useContextFromLayer(MyAsyncLayer)
* const Sub = yield* SubComponent.use.pipe(
* Effect.provide(context)
* )
* const context = yield* Component.useLayer(MyAsyncLayer)
* const Sub = yield* Effect.provide(SubComponent, context)
*
* return <Sub />
* }).pipe(
* Async.async // Required to handle async layer effects
* )
*/
export const useContextFromLayer = <ROut, E, RIn>(
export const useLayer = <ROut, E, RIn>(
layer: Layer.Layer<ROut, E, RIn>,
options?: useContext.Options,
): Effect.Effect<Context.Context<ROut>, E, RIn | Scope.Scope> => useOnChange(() => Effect.flatMap(
@@ -317,7 +317,7 @@ describe("Component", () => {
() => Layer.succeed(ValueService, { value: props.value }),
[props.value],
)
const context = yield* Component.useContextFromLayer(serviceLayer, {
const context = yield* Component.useLayer(serviceLayer, {
finalizerExecutionDebounce: 0,
})
const Child = yield* Effect.provide(SubComponent.use, context)
+1 -1
View File
@@ -42,7 +42,7 @@ export class TodosView extends Component.make("TodosView")(function*() {
}) {}
const Index = Component.make("IndexView")(function*() {
const context = yield* Component.useContextFromLayer(TodosState.Default)
const context = yield* Component.useLayer(TodosState.Default)
const Todos = yield* Effect.provide(TodosView.use, context)
return <Todos />
+7 -13
View File
@@ -1075,10 +1075,8 @@ export declare namespace useContext {
* ```tsx
* const MyLayer = Layer.succeed(MyService, new MyServiceImpl())
* const MyComponent = Component.make(function*() {
* const context = yield* Component.useContextFromLayer(MyLayer)
* const Sub = yield* SubComponent.use.pipe(
* Effect.provide(context)
* )
* const context = yield* Component.useLayer(MyLayer)
* const Sub = yield* Effect.provide(SubComponent.use, context)
*
* return <Sub />
* })
@@ -1087,12 +1085,10 @@ export declare namespace useContext {
* @example With memoized layer
* ```tsx
* const MyComponent = Component.make(function*(props: { id: string })) {
* const context = yield* Component.useContextFromLayer(
* const context = yield* Component.useLayer(
* React.useMemo(() => Layer.succeed(MyService, new MyServiceImpl(props.id)), [props.id])
* )
* const Sub = yield* SubComponent.use.pipe(
* Effect.provide(context)
* )
* const Sub = yield* Effect.provide(SubComponent.use, context)
*
* return <Sub />
* })
@@ -1102,17 +1098,15 @@ export declare namespace useContext {
* ```tsx
* const MyAsyncLayer = Layer.effect(MyService, someAsyncEffect)
* const MyComponent = Component.make(function*() {
* const context = yield* Component.useContextFromLayer(MyAsyncLayer)
* const Sub = yield* SubComponent.use.pipe(
* Effect.provide(context)
* )
* const context = yield* Component.useLayer(MyAsyncLayer)
* const Sub = yield* Effect.provide(SubComponent.use, context)
*
* return <Sub />
* }).pipe(
* Async.async // Required to handle async layer effects
* )
*/
export const useContextFromLayer = <ROut, E, RIn>(
export const useLayer = <ROut, E, RIn>(
layer: Layer.Layer<ROut, E, RIn>,
options?: useContext.Options,
): Effect.Effect<Context.Context<ROut>, E, RIn | Scope.Scope> => useOnChange(() => Effect.context<RIn>().pipe(
+1 -1
View File
@@ -317,7 +317,7 @@ describe("Component", () => {
() => Layer.succeed(ValueService, { value: props.value }),
[props.value],
)
const context = yield* Component.useContextFromLayer(serviceLayer, {
const context = yield* Component.useLayer(serviceLayer, {
finalizerExecutionDebounce: 0,
})
const Child = yield* Effect.provide(SubComponent.use, context)
@@ -47,7 +47,7 @@ const LensFormPageView = Component.make("LensFormPageView")(function*() {
yield* Console.log("LensForm route mounted")
}))
const context = yield* Component.useContextFromLayer(AppState.layer)
const context = yield* Component.useLayer(AppState.layer)
const UserProfileEditor = yield* Effect.provide(UserProfileEditorView.use, context)
return <UserProfileEditor />
+1 -1
View File
@@ -5,7 +5,7 @@
"private": true,
"scripts": {
"dev": "vite",
"lint:tsc": "tsc --noEmit",
"lint:tsc": "tsc -b --noEmit",
"lint:biome": "biome lint",
"preview": "vite preview",
"clean:cache": "rm -rf .turbo node_modules/.tmp node_modules/.vite* .tanstack",
+1 -1
View File
@@ -23,7 +23,7 @@ const SubComponent = Component.makeUntraced("SubComponent")(function*() {
const ContextView = Component.makeUntraced("ContextView")(function*() {
const [serviceValue, setServiceValue] = React.useState("test")
const SubServiceLayer = React.useMemo(() => SubService.Default(serviceValue), [serviceValue])
const SubComponentFC = yield* Effect.provide(SubComponent.use, yield* Component.useContextFromLayer(SubServiceLayer))
const SubComponentFC = yield* Effect.provide(SubComponent.use, yield* Component.useLayer(SubServiceLayer))
return (
<Container>
+1 -1
View File
@@ -129,7 +129,7 @@ class RegisterFormView extends Component.make("RegisterFormView")(function*() {
const RegisterPage = Component.make("RegisterPageView")(function*() {
const RegisterForm = yield* Effect.provide(
RegisterFormView.use,
yield* Component.useContextFromLayer(RegisterFormService.Default),
yield* Component.useLayer(RegisterFormService.Default),
)
return <RegisterForm />
+1 -1
View File
@@ -11,7 +11,7 @@ const TodosStateLive = TodosState.Default("todos")
const Index = Component.make("IndexView")(function*() {
const Todos = yield* Effect.provide(
TodosView.use,
yield* Component.useContextFromLayer(TodosStateLive),
yield* Component.useLayer(TodosStateLive),
)
return <Todos />
-1
View File
@@ -22,7 +22,6 @@
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true,
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
},