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< class ProfileState extends Context.Service<
ProfileState, ProfileState,
{ { readonly profile: Lens.Lens<UserProfile> }
readonly profile: Lens.Lens<UserProfile>
readonly name: Lens.Lens<string>
readonly role: Lens.Lens<string>
}
>()("ProfileState") { >()("ProfileState") {
static readonly layer = Layer.effect( static readonly layer = Layer.effect(
ProfileState, 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 ProfileNameView = Component.make("ProfileName")(function*() {
const state = yield* ProfileState const state = yield* ProfileState
const [name, setName] = yield* Lens.useState(state.name) const [nameLens, roleLens, cityLens] = yield* Component.useOnMount(() =>
const [role] = yield* View.useAll([state.role]) 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 ( return (
<label> <label>
@@ -270,27 +276,21 @@ const ProfileNameView = Component.make("ProfileName")(function*() {
onChange={(event) => setName(event.currentTarget.value)} onChange={(event) => setName(event.currentTarget.value)}
/> />
<span>Role: {role}</span> <span>Role: {role}</span>
<span>City: {city}</span>
</label> </label>
) )
}) })
``` ```
Updating the focused `name` Lens through `Lens.useState` updates the parent The service owns only the root `profile` Lens. The component chooses the fields
`profile` Lens. The focused `role` Lens is only read, so it stays on the simpler it needs, creates those focused Lenses once in `Component.useOnMount`, and
`View.useAll` path. 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 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 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: or index. The `cityLens` above uses the curried form with `pipe` to focus through
a deeper path without storing intermediate Lenses.
```tsx
// profileLens is a Lens.Lens<UserProfile>
const cityLens = profileLens.pipe(
Lens.focusObjectOn("contact"),
Lens.focusObjectOn("address"),
Lens.focusObjectOn("city"),
)
```
For focusing into nested state, deriving lenses, custom write behavior, and the For focusing into nested state, deriving lenses, custom write behavior, and the
complete API, refer to the complete API, refer to the