--- sidebar_position: 2 title: State Management --- # State Management `Lens` is the main type used for state management in `effect-fc`. A Lens is an effectful handle to a piece of state. It can read the current value, subscribe to changes, and write updates back to the underlying source. A Lens can point at a whole state object or focus on one nested field inside it. The usual pattern is to create state with Effect primitives such as `SubscriptionRef`, turn that primitive into a Lens with a matching constructor such as `Lens.fromSubscriptionRef`, and bind Lens values into components with `Subscribable.useAll`. `Subscribable` is the read-only side of this model. Every Lens is also a Subscribable. `effect-fc` re-exports the `Lens` and `Subscribable` modules from [`effect-lens`](https://github.com/Thiladev/effect-lens) for convenience. The core data model and transformation APIs belong to `effect-lens`, so check the [`effect-lens` documentation](https://github.com/Thiladev/effect-lens/tree/master/packages/effect-lens) for the full Lens/Subscribable API. ## Where To Store State State can live pretty much anywhere as a `Lens` or `Subscribable`: in a service, in a layer, in a component scope, or alongside plain React state. Pick the owner based on who needs the state. Once you have a Lens/Subscribable handle, pass it around however you like, including through React props. If state is shared by multiple components or belongs to application logic, store it in an Effect service: ```tsx import { Effect, SubscriptionRef } from "effect" import { Component, Lens, Subscribable } from "effect-fc" class CounterState extends Effect.Service()("CounterState", { effect: Effect.gen(function* () { const count = Lens.fromSubscriptionRef(yield* SubscriptionRef.make(0)) return { count } as const }), }) {} const CounterValueView = Component.make("CounterValue")(function* () { const state = yield* CounterState const [count] = yield* Subscribable.useAll([state.count]) return

Count: {count}

}) ``` If state belongs to a single Effect-FC component instance, or to a shallow hierarchy of subcomponents that receive it through props, create it with `Component.useOnMount`: ```tsx import { Effect, SubscriptionRef } from "effect" import { Component, Lens, Subscribable } from "effect-fc" const LocalCounterView = Component.make("LocalCounter")(function* () { const state = yield* Component.useOnMount(() => Effect.gen(function* () { const count = Lens.fromSubscriptionRef(yield* SubscriptionRef.make(0)) return { count } as const }), ) const [count] = yield* Subscribable.useAll([state.count]) return

Count: {count}

}) ``` For simple UI state that is not shared and does not need Effect integration, prefer regular React state. A local "show details" toggle is usually better as `React.useState(false)` than as a Lens. ## Subscribable.useAll A `Subscribable` is reactive state with a current value and a stream of changes. Use `Subscribable.useAll` whenever a component needs to bind subscribable values into render output. `Lens` is a `Subscribable`, so this is also the default way to read Lens values from a component. ```tsx import { Effect, SubscriptionRef } from "effect" import { Component, Lens, Subscribable } from "effect-fc" class CounterState extends Effect.Service()("CounterState", { effect: Effect.gen(function* () { const count = Lens.fromSubscriptionRef(yield* SubscriptionRef.make(0)) const doubled = Subscribable.map(count, (n) => n * 2) return { count, doubled } as const }), }) {} const CounterReadOnlyView = Component.make("CounterReadOnly")( function* () { const state = yield* CounterState const [count, doubled] = yield* Subscribable.useAll([ state.count, state.doubled, ]) return

Count: {count}, doubled: {doubled}

}, ) ``` `Subscribable.useAll` reads the current values during render and uses scoped subscriptions to update React state when changes arrive. When you need to modify state, write to the Lens with `Lens.set` or `Lens.update`: ```tsx const CounterControlsView = Component.make("CounterControls")( function* () { const state = yield* CounterState const [count] = yield* Subscribable.useAll([state.count]) const increment = yield* Component.useCallbackSync( () => Lens.update(state.count, (n) => n + 1), [], ) const reset = yield* Component.useCallbackSync( () => Lens.set(state.count, 0), [], ) return (

Count: {count}

) }, ) ``` ## Lens.useState `Lens.useState` is useful when React needs the familiar `[value, setValue]` tuple, backed by a Lens. Reach for it when the JSX API expects a synchronous setter, especially controlled inputs such as text fields, checkboxes, selects, or third-party components with `value` / `onChange` props. If a component only needs to display the value, prefer `Subscribable.useAll`. `Lens.useState` is for places where reading and writing need to be wired together in React's local-state shape. ```tsx import { Effect, SubscriptionRef } from "effect" import { Component, Lens } from "effect-fc" class FormState extends Effect.Service()("FormState", { effect: Effect.gen(function* () { const name = Lens.fromSubscriptionRef(yield* SubscriptionRef.make("")) return { name } as const }), }) {} const NameInputView = Component.make("NameInput")(function* () { const state = yield* FormState const [name, setName] = yield* Lens.useState(state.name) return ( setName(event.currentTarget.value)} /> ) }) ``` `Lens.useState` returns the current value and a React-compatible setter. Calling the setter writes through the Lens, so every other component subscribed to the same Lens sees the update. ## Focused Lenses Use focused Lenses when a component should work with one part of a larger state object. A focused Lens is still a Lens, so it can be read with `Subscribable.useAll` or used with `Lens.useState` when React needs a read/write tuple. ```tsx import { Effect, SubscriptionRef } from "effect" import { Component, Lens, Subscribable } from "effect-fc" interface UserProfile { readonly name: string readonly email: string readonly role: string } class ProfileState extends Effect.Service()("ProfileState", { effect: Effect.gen(function* () { const profile = Lens.fromSubscriptionRef( yield* SubscriptionRef.make({ name: "", email: "", role: "reader", }), ) const name = Lens.focusObjectOn(profile, "name") const role = Lens.focusObjectOn(profile, "role") return { profile, name, role } as const }), }) {} const ProfileNameView = Component.make("ProfileName")(function* () { const state = yield* ProfileState const [name, setName] = yield* Lens.useState(state.name) const [role] = yield* Subscribable.useAll([state.role]) return ( ) }) ``` 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 `Subscribable.useAll` path. For focusing into nested state, deriving lenses, custom write behavior, and the complete API, refer to the [`effect-lens` documentation](https://github.com/Thiladev/effect-lens/tree/master/packages/effect-lens).