Finalize the Effect View rename and refresh docs, examples, and tooling #59
@@ -52,6 +52,14 @@ valid decoded value should go:
|
|||||||
| `MutationForm` | Yes | It is passed to a mutation when `submit` runs. |
|
| `MutationForm` | Yes | It is passed to a mutation when `submit` runs. |
|
||||||
| `LensForm` | Yes | It is written automatically to a target `Lens`. |
|
| `LensForm` | Yes | It is written automatically to a target `Lens`. |
|
||||||
|
|
||||||
|
## Create forms once
|
||||||
|
|
||||||
|
`MutationForm.service` and `LensForm.service` are Effect constructors, not
|
||||||
|
hooks. Create each root form once and keep it stable. For a component-owned
|
||||||
|
form, call the constructor inside `Component.useOnMount`; for a form shared by
|
||||||
|
multiple components, create it in an Effect service. Update the existing form's
|
||||||
|
Lenses rather than reconstructing the form during render.
|
||||||
|
|
||||||
## MutationForm: validate, then submit
|
## MutationForm: validate, then submit
|
||||||
|
|
||||||
Use `MutationForm` for registration, checkout, search, and other workflows with
|
Use `MutationForm` for registration, checkout, search, and other workflows with
|
||||||
@@ -102,9 +110,9 @@ mutation receives the schema's decoded profile, so `profile.age` is a number.
|
|||||||
Schema transformations happen before the mutation and schema issues prevent an
|
Schema transformations happen before the mutation and schema issues prevent an
|
||||||
invalid draft from being submitted.
|
invalid draft from being submitted.
|
||||||
|
|
||||||
`MutationForm.service` also starts initial validation in the current scope. Use
|
`MutationForm.service` also starts initial validation in the current scope. In
|
||||||
it inside `Component.useOnMount`, a scoped service, or another Effect scope so
|
the example above, `Component.useOnMount` keeps that validation and the form's
|
||||||
its validation and mutation work is cleaned up with its owner.
|
mutation work tied to the component that owns them.
|
||||||
|
|
||||||
## LensForm: validate, then synchronize
|
## LensForm: validate, then synchronize
|
||||||
|
|
||||||
|
|||||||
@@ -295,24 +295,25 @@ const ResourceView = Component.make("Resource")(function* () {
|
|||||||
with the context already available in the component body, so the resource above
|
with the context already available in the component body, so the resource above
|
||||||
is owned by the component's root scope.
|
is owned by the component's root scope.
|
||||||
|
|
||||||
`useOnChange`, `useReactEffect`, and `useReactLayoutEffect` each create a scope
|
`useOnChange`, `useReactEffect`, and `useReactLayoutEffect` each manage their
|
||||||
that can be replaced without closing the component root scope.
|
own resources. When their dependencies change, those resources are cleaned up
|
||||||
|
and recreated while the rest of the component remains active.
|
||||||
|
|
||||||
The main lifecycle choices are:
|
The main lifecycle choices are:
|
||||||
|
|
||||||
| Hook | What it does | Scope seen by setup | Scope closes when |
|
| Hook | What it does | Scope seen by setup | Scope closes when |
|
||||||
| --- | --- | --- | --- |
|
| --- | --- | --- | --- |
|
||||||
| Component body | Produces the component's rendered value | Component root scope | The component unmounts |
|
| Component body | Produces the component's rendered value | Component root scope | The component unmounts |
|
||||||
| `useOnMount` | Computes and caches a value for the component instance | The same component root scope | The component unmounts |
|
| `useOnMount` | Computes and caches a value for the component instance | Component root scope | The component unmounts |
|
||||||
| `useOnChange` | Recomputes a cached value when dependencies change | A new dependency scope | Dependencies change or the component unmounts |
|
| `useOnChange` | Recomputes a cached value when dependencies change | A new dependency scope | Dependencies change or the component unmounts |
|
||||||
| `useReactEffect` | Runs a post-commit side effect | A new effect scope | Dependencies change or the component unmounts |
|
| `useReactEffect` | Runs a post-commit side effect | A new effect scope | Dependencies change or the component unmounts |
|
||||||
| `useReactLayoutEffect` | Runs a layout effect before the browser paints | A new layout-effect scope | Dependencies change or the component unmounts |
|
| `useReactLayoutEffect` | Runs a layout effect before the browser paints | A new layout-effect scope | Dependencies change or the component unmounts |
|
||||||
| `useLayer` | Builds and provides a layer context | A dependency scope created through `useOnChange` | The layer reference changes or the component unmounts |
|
| `useLayer` | Builds and provides a layer context | A new scope tied to the current layer reference | The layer reference changes or the component unmounts |
|
||||||
|
|
||||||
`useRunSync`, `useRunPromise`, `useCallbackSync`, and `useCallbackPromise` do
|
`useRunSync` and `useRunPromise` do not create a new scope. The runner they
|
||||||
not create lifecycle scopes either. They capture the component context, so an
|
return includes the component root scope in its context by default.
|
||||||
Effect invoked through them sees the component root scope unless it explicitly
|
`useCallbackSync` and `useCallbackPromise` similarly capture the component
|
||||||
provides another one.
|
context required by their Effect.
|
||||||
|
|
||||||
### useOnMount
|
### useOnMount
|
||||||
|
|
||||||
@@ -410,6 +411,21 @@ Use `Component.useRunSync` only for Effects known to complete synchronously.
|
|||||||
Use `Component.useRunPromise` for Effects that may suspend, sleep, fetch, or
|
Use `Component.useRunPromise` for Effects that may suspend, sleep, fetch, or
|
||||||
otherwise continue asynchronously.
|
otherwise continue asynchronously.
|
||||||
|
|
||||||
|
Both runners provide `Scope.Scope` by default, so they can run scoped Effects
|
||||||
|
without a type argument. When an Effect also requires application services,
|
||||||
|
declare the complete runner context explicitly:
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
import { Scope } from "effect"
|
||||||
|
|
||||||
|
const runPromise = yield* Component.useRunPromise<
|
||||||
|
Scope.Scope | UserRepository
|
||||||
|
>()
|
||||||
|
```
|
||||||
|
|
||||||
|
The resulting runner accepts Effects that require the component scope,
|
||||||
|
`UserRepository`, or both.
|
||||||
|
|
||||||
When a callback is passed to a memoized child or used as a dependency, use the
|
When a callback is passed to a memoized child or used as a dependency, use the
|
||||||
callback variants to preserve its identity:
|
callback variants to preserve its identity:
|
||||||
|
|
||||||
|
|||||||
@@ -30,6 +30,11 @@ Create a mutation in a component scope with `Mutation.make`. Its function can
|
|||||||
be any Effect, including one that requires services from the application
|
be any Effect, including one that requires services from the application
|
||||||
runtime:
|
runtime:
|
||||||
|
|
||||||
|
`Mutation.make` is an Effect constructor, not a hook. Create each mutation
|
||||||
|
instance once and keep it stable instead of calling `Mutation.make` again on
|
||||||
|
every render. Use `Component.useOnMount` for a component-owned mutation or
|
||||||
|
create it in an Effect service when it should be shared.
|
||||||
|
|
||||||
```tsx
|
```tsx
|
||||||
import { Effect } from "effect"
|
import { Effect } from "effect"
|
||||||
import { AsyncResult } from "effect/unstable/reactivity"
|
import { AsyncResult } from "effect/unstable/reactivity"
|
||||||
|
|||||||
@@ -35,7 +35,6 @@ application runtime once, alongside the services used by your query effects:
|
|||||||
|
|
||||||
```tsx title="src/runtime.ts"
|
```tsx title="src/runtime.ts"
|
||||||
import { Layer } from "effect"
|
import { Layer } from "effect"
|
||||||
import { FetchHttpClient } from "effect/unstable/http"
|
|
||||||
import { QueryClient, ReactRuntime } from "effect-view"
|
import { QueryClient, ReactRuntime } from "effect-view"
|
||||||
|
|
||||||
const AppLive = Layer.empty.pipe(
|
const AppLive = Layer.empty.pipe(
|
||||||
@@ -44,7 +43,6 @@ const AppLive = Layer.empty.pipe(
|
|||||||
defaultRefreshOnWindowFocus: true,
|
defaultRefreshOnWindowFocus: true,
|
||||||
cacheGcTime: "5 minutes",
|
cacheGcTime: "5 minutes",
|
||||||
})),
|
})),
|
||||||
Layer.provideMerge(FetchHttpClient.layer),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
export const runtime = ReactRuntime.make(AppLive)
|
export const runtime = ReactRuntime.make(AppLive)
|
||||||
@@ -60,6 +58,12 @@ A query is driven by a `View` rather than by a value read during one React
|
|||||||
render. Whenever that key changes, `Query.service` checks the cache and starts
|
render. Whenever that key changes, `Query.service` checks the cache and starts
|
||||||
the query effect when necessary.
|
the query effect when necessary.
|
||||||
|
|
||||||
|
`Query.service` is an Effect constructor, not a hook. Create each query instance
|
||||||
|
once and keep it stable. In a component, the usual place is
|
||||||
|
`Component.useOnMount`; a query shared by multiple components can instead be
|
||||||
|
owned by an Effect service. Change the existing query's reactive key rather
|
||||||
|
than reconstructing the query during render.
|
||||||
|
|
||||||
```tsx
|
```tsx
|
||||||
import { Effect, Schema, SubscriptionRef } from "effect"
|
import { Effect, Schema, SubscriptionRef } from "effect"
|
||||||
import { HttpClient } from "effect/unstable/http"
|
import { HttpClient } from "effect/unstable/http"
|
||||||
@@ -118,9 +122,9 @@ Effect equality by default, so structurally equal Effect data types work well
|
|||||||
as query keys. Supply `keyEquivalence` when the key needs different equality
|
as query keys. Supply `keyEquivalence` when the key needs different equality
|
||||||
semantics.
|
semantics.
|
||||||
|
|
||||||
`Query.service` creates the query and starts watching its key in the current
|
`Query.service` starts watching its key in the current scope. The
|
||||||
scope. Creating it in `Component.useOnMount` keeps one query instance—and one
|
`Component.useOnMount` call above keeps both the query instance and its query
|
||||||
stable query function identity—for the component's lifetime.
|
function identity stable for the component's lifetime.
|
||||||
|
|
||||||
## Render AsyncResult
|
## Render AsyncResult
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user