Finalize the Effect View rename and refresh docs, examples, and tooling #59

Merged
Thilawyn merged 12 commits from next into master 2026-07-27 04:11:42 +02:00
Showing only changes of commit 1238db9443 - Show all commits
+23 -23
View File
@@ -228,11 +228,7 @@ interface UserProfile {
class ProfileState extends Context.Service<
ProfileState,
{
readonly profile: Lens.Lens<UserProfile>
readonly name: Lens.Lens<string>
readonly role: Lens.Lens<string>
}
{ readonly profile: Lens.Lens<UserProfile> }
>()("ProfileState") {
static readonly layer = Layer.effect(
ProfileState,
@@ -249,18 +245,28 @@ class ProfileState extends Context.Service<
},
}),
)
const name = Lens.focusObjectOn(profile, "name")
const role = Lens.focusObjectOn(profile, "role")
return { profile, name, role } as const
return { profile } as const
}),
)
}
const ProfileNameView = Component.make("ProfileName")(function*() {
const state = yield* ProfileState
const [name, setName] = yield* Lens.useState(state.name)
const [role] = yield* View.useAll([state.role])
const [nameLens, roleLens, cityLens] = yield* Component.useOnMount(() =>
Effect.succeed([
Lens.focusObjectOn(state.profile, "name"),
Lens.focusObjectOn(state.profile, "role"),
state.profile.pipe(
Lens.focusObjectOn("contact"),
Lens.focusObjectOn("address"),
Lens.focusObjectOn("city"),
),
] as const),
)
const [name, setName] = yield* Lens.useState(nameLens)
const [role, city] = yield* View.useAll([roleLens, cityLens])
return (
<label>
@@ -270,27 +276,21 @@ const ProfileNameView = Component.make("ProfileName")(function*() {
onChange={(event) => setName(event.currentTarget.value)}
/>
<span>Role: {role}</span>
<span>City: {city}</span>
</label>
)
})
```
Updating the focused `name` Lens through `Lens.useState` updates the parent
`profile` Lens. The focused `role` Lens is only read, so it stays on the simpler
`View.useAll` path.
The service owns only the root `profile` Lens. The component chooses the fields
it needs, creates those focused Lenses once in `Component.useOnMount`, and
subscribes only to them. Updating `nameLens` through `Lens.useState` still
updates the parent `profile` Lens.
Lens focus helpers have a dual API. They can be called in data-first form, such
as `Lens.focusObjectOn(profile, "name")`, or in curried form with only the key
or index. The curried form composes naturally with `pipe` for deeper paths:
```tsx
// profileLens is a Lens.Lens<UserProfile>
const cityLens = profileLens.pipe(
Lens.focusObjectOn("contact"),
Lens.focusObjectOn("address"),
Lens.focusObjectOn("city"),
)
```
or index. The `cityLens` above uses the curried form with `pipe` to focus through
a deeper path without storing intermediate Lenses.
For focusing into nested state, deriving lenses, custom write behavior, and the
complete API, refer to the