Add explicit lifecycle pipelines and scheduled query refreshes #68
@@ -82,7 +82,7 @@ const CreateProfileView = Component.make("CreateProfile")(function* () {
|
|||||||
Effect.log(
|
Effect.log(
|
||||||
`Creating ${profile.displayName}, age ${profile.age}`,
|
`Creating ${profile.displayName}, age ${profile.age}`,
|
||||||
),
|
),
|
||||||
}).pipe(MutationForm.run),
|
}).pipe(MutationForm.thenRun),
|
||||||
)
|
)
|
||||||
|
|
||||||
const [canCommit, isCommitting] = yield* View.useAll([
|
const [canCommit, isCommitting] = yield* View.useAll([
|
||||||
@@ -108,7 +108,7 @@ 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.run` starts initial validation. In the example above,
|
`MutationForm.thenRun` starts initial validation. In the example above,
|
||||||
`Component.useOnMount` keeps that validation and the form's mutation work tied
|
`Component.useOnMount` keeps that validation and the form's mutation work tied
|
||||||
to the component that owns them.
|
to the component that owns them.
|
||||||
|
|
||||||
@@ -137,7 +137,7 @@ const EditProfileView = Component.make("EditProfile")(function* () {
|
|||||||
const form = yield* LensForm.make({
|
const form = yield* LensForm.make({
|
||||||
schema: ProfileSchema,
|
schema: ProfileSchema,
|
||||||
target: profile,
|
target: profile,
|
||||||
}).pipe(LensForm.run)
|
}).pipe(LensForm.thenRun)
|
||||||
|
|
||||||
return [form, profile] as const
|
return [form, profile] as const
|
||||||
}),
|
}),
|
||||||
@@ -432,7 +432,7 @@ const AppointmentView = Component.make("Appointment")(function* () {
|
|||||||
Effect.log(
|
Effect.log(
|
||||||
`Saving ${DateTime.formatIso(appointment.startsAt)}`,
|
`Saving ${DateTime.formatIso(appointment.startsAt)}`,
|
||||||
),
|
),
|
||||||
}).pipe(MutationForm.run)
|
}).pipe(MutationForm.thenRun)
|
||||||
|
|
||||||
return [form, Form.focusObjectOn(form, "startsAt")] as const
|
return [form, Form.focusObjectOn(form, "startsAt")] as const
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ A query is driven by a `View` rather than by a value read during one React
|
|||||||
render. Whenever that key changes, a running Query checks the cache and starts
|
render. Whenever that key changes, a running Query checks the cache and starts
|
||||||
the query effect when necessary.
|
the query effect when necessary.
|
||||||
|
|
||||||
`Query.make` constructs the Query and `Query.run` starts it in the current
|
`Query.make` constructs the Query and `Query.thenRun` starts it in the current
|
||||||
scope. Create each query instance once and keep it stable. In a component, the usual place is
|
scope. 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
|
`Component.useOnMount`; a query shared by multiple components can instead be
|
||||||
owned by an Effect service. Change the existing query's reactive key rather
|
owned by an Effect service. Change the existing query's reactive key rather
|
||||||
@@ -95,7 +95,7 @@ const PostView = Component.make("Post")(function* () {
|
|||||||
Effect.andThen((response) => response.json),
|
Effect.andThen((response) => response.json),
|
||||||
Effect.andThen(Schema.decodeUnknownEffect(Post)),
|
Effect.andThen(Schema.decodeUnknownEffect(Post)),
|
||||||
),
|
),
|
||||||
}).pipe(Query.run)
|
}).pipe(Query.thenRun)
|
||||||
|
|
||||||
return [Lens.focusTupleAt(key, 1), query] as const
|
return [Lens.focusTupleAt(key, 1), query] as const
|
||||||
}),
|
}),
|
||||||
@@ -124,7 +124,7 @@ 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.run` starts watching its key in the current scope. The
|
`Query.thenRun` starts watching its key in the current scope. The
|
||||||
`Component.useOnMount` call above keeps both the query instance and its query
|
`Component.useOnMount` call above keeps both the query instance and its query
|
||||||
function identity stable for the component's lifetime.
|
function identity stable for the component's lifetime.
|
||||||
|
|
||||||
@@ -256,7 +256,7 @@ Refresh every five minutes, starting after five minutes:
|
|||||||
import { Schedule } from "effect"
|
import { Schedule } from "effect"
|
||||||
|
|
||||||
const query = yield* Query.make(options).pipe(
|
const query = yield* Query.make(options).pipe(
|
||||||
Query.run,
|
Query.thenRun,
|
||||||
Query.withScheduledRefresh(Schedule.spaced("5 minutes")),
|
Query.withScheduledRefresh(Schedule.spaced("5 minutes")),
|
||||||
)
|
)
|
||||||
```
|
```
|
||||||
@@ -265,7 +265,7 @@ Limit the number of refreshes:
|
|||||||
|
|
||||||
```ts
|
```ts
|
||||||
const query = yield* Query.make(options).pipe(
|
const query = yield* Query.make(options).pipe(
|
||||||
Query.run,
|
Query.thenRun,
|
||||||
Query.withScheduledRefresh(
|
Query.withScheduledRefresh(
|
||||||
Schedule.spaced("5 minutes").pipe(
|
Schedule.spaced("5 minutes").pipe(
|
||||||
Schedule.upTo({ times: 3 }),
|
Schedule.upTo({ times: 3 }),
|
||||||
@@ -296,7 +296,7 @@ const query = yield* Query.make({
|
|||||||
f: loadPost,
|
f: loadPost,
|
||||||
staleTime: "10 seconds",
|
staleTime: "10 seconds",
|
||||||
refreshOnWindowFocus: false,
|
refreshOnWindowFocus: false,
|
||||||
}).pipe(Query.run)
|
}).pipe(Query.thenRun)
|
||||||
```
|
```
|
||||||
|
|
||||||
Window-focus refresh depends on the optional `@effect/platform-browser`
|
Window-focus refresh depends on the optional `@effect/platform-browser`
|
||||||
|
|||||||
@@ -189,7 +189,7 @@ export const make = Effect.fnUntraced(function* <A, I = A, RD = never, RE = neve
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
export const run = <A, I = A, RD = never, RE = never, TER = never, TEW = never, TRR = never, TRW = never, E = never, R = never>(
|
export const thenRun = <A, I = A, RD = never, RE = never, TER = never, TEW = never, TRR = never, TRW = never, E = never, R = never>(
|
||||||
self: Effect.Effect<LensForm<A, I, RD, RE, TER, TEW, TRR, TRW>, E, R>,
|
self: Effect.Effect<LensForm<A, I, RD, RE, TER, TEW, TRR, TRW>, E, R>,
|
||||||
): Effect.Effect<
|
): Effect.Effect<
|
||||||
LensForm<A, I, RD, RE, TER, TEW, TRR, TRW>,
|
LensForm<A, I, RD, RE, TER, TEW, TRR, TRW>,
|
||||||
|
|||||||
@@ -196,7 +196,7 @@ export const make = Effect.fnUntraced(function* <A, I = A, RD = never, RE = neve
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
export const run = <A, I = A, RD = never, RE = never, MA = void, ME = never, MR = never, E = never, R = never>(
|
export const thenRun = <A, I = A, RD = never, RE = never, MA = void, ME = never, MR = never, E = never, R = never>(
|
||||||
self: Effect.Effect<MutationForm<A, I, RD, RE, MA, ME, MR>, E, R>,
|
self: Effect.Effect<MutationForm<A, I, RD, RE, MA, ME, MR>, E, R>,
|
||||||
): Effect.Effect<
|
): Effect.Effect<
|
||||||
MutationForm<A, I, RD, RE, MA, ME, MR>,
|
MutationForm<A, I, RD, RE, MA, ME, MR>,
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ describe("Query", () => {
|
|||||||
}),
|
}),
|
||||||
staleTime: "0 millis",
|
staleTime: "0 millis",
|
||||||
}).pipe(
|
}).pipe(
|
||||||
Query.run,
|
Query.thenRun,
|
||||||
Query.withScheduledRefresh(
|
Query.withScheduledRefresh(
|
||||||
Schedule.spaced("1 second").pipe(
|
Schedule.spaced("1 second").pipe(
|
||||||
Schedule.upTo({ times: 1 }),
|
Schedule.upTo({ times: 1 }),
|
||||||
@@ -187,7 +187,7 @@ describe("Query", () => {
|
|||||||
return `value:${id}:${calls}`
|
return `value:${id}:${calls}`
|
||||||
}),
|
}),
|
||||||
staleTime: "1 minute",
|
staleTime: "1 minute",
|
||||||
}).pipe(Query.run)
|
}).pipe(Query.thenRun)
|
||||||
|
|
||||||
const latestFinalState = yield* Effect.sleep("1 millis").pipe(
|
const latestFinalState = yield* Effect.sleep("1 millis").pipe(
|
||||||
Effect.andThen(View.get(query.latestFinalState)),
|
Effect.andThen(View.get(query.latestFinalState)),
|
||||||
|
|||||||
@@ -410,7 +410,7 @@ export const make = Effect.fnUntraced(function* <K, A, E = never, R = never>(
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
export const run = <K, A, E = never, R = never, E2 = never, R2 = never>(
|
export const thenRun = <K, A, E = never, R = never, E2 = never, R2 = never>(
|
||||||
self: Effect.Effect<Query<K, A, E, R>, E2, R2>,
|
self: Effect.Effect<Query<K, A, E, R>, E2, R2>,
|
||||||
): Effect.Effect<Query<K, A, E, R>, E2, Scope.Scope | R2> => Effect.tap(
|
): Effect.Effect<Query<K, A, E, R>, E2, Scope.Scope | R2> => Effect.tap(
|
||||||
self,
|
self,
|
||||||
@@ -423,7 +423,7 @@ export const run = <K, A, E = never, R = never, E2 = never, R2 = never>(
|
|||||||
* @example Refresh every five minutes, starting after five minutes
|
* @example Refresh every five minutes, starting after five minutes
|
||||||
* ```ts
|
* ```ts
|
||||||
* yield* Query.make(options).pipe(
|
* yield* Query.make(options).pipe(
|
||||||
* Query.run,
|
* Query.thenRun,
|
||||||
* Query.withScheduledRefresh(Schedule.spaced("5 minutes")),
|
* Query.withScheduledRefresh(Schedule.spaced("5 minutes")),
|
||||||
* )
|
* )
|
||||||
* ```
|
* ```
|
||||||
@@ -431,7 +431,7 @@ export const run = <K, A, E = never, R = never, E2 = never, R2 = never>(
|
|||||||
* @example Refresh at most three times
|
* @example Refresh at most three times
|
||||||
* ```ts
|
* ```ts
|
||||||
* yield* Query.make(options).pipe(
|
* yield* Query.make(options).pipe(
|
||||||
* Query.run,
|
* Query.thenRun,
|
||||||
* Query.withScheduledRefresh(
|
* Query.withScheduledRefresh(
|
||||||
* Schedule.spaced("5 minutes").pipe(Schedule.upTo({ times: 3 })),
|
* Schedule.spaced("5 minutes").pipe(Schedule.upTo({ times: 3 })),
|
||||||
* ),
|
* ),
|
||||||
|
|||||||
@@ -115,14 +115,14 @@ export const make = Effect.fnUntraced(function* (
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
export const run = <E = never, R = never>(
|
export const thenRun = <E = never, R = never>(
|
||||||
self: Effect.Effect<QueryClientService, E, R>,
|
self: Effect.Effect<QueryClientService, E, R>,
|
||||||
): Effect.Effect<QueryClientService, E, Scope.Scope | R> => Effect.tap(
|
): Effect.Effect<QueryClientService, E, Scope.Scope | R> => Effect.tap(
|
||||||
self,
|
self,
|
||||||
client => Effect.forkScoped(client.run),
|
client => Effect.forkScoped(client.run),
|
||||||
)
|
)
|
||||||
|
|
||||||
export const layer = (options?: make.Options) => Layer.effect(QueryClient, run(make(options)))
|
export const layer = (options?: make.Options) => Layer.effect(QueryClient, thenRun(make(options)))
|
||||||
|
|
||||||
|
|
||||||
export const QueryClientCacheKeyTypeId: unique symbol = Symbol.for("@effect-view/QueryClient/QueryClientCacheKey")
|
export const QueryClientCacheKeyTypeId: unique symbol = Symbol.for("@effect-view/QueryClient/QueryClientCacheKey")
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ const RegisterRouteComponent = Component.make("RegisterRouteView")(function*() {
|
|||||||
initialEncodedValue: { email: "", password: "" },
|
initialEncodedValue: { email: "", password: "" },
|
||||||
f: ([value]) => Effect.log(`Registered ${value.email}`),
|
f: ([value]) => Effect.log(`Registered ${value.email}`),
|
||||||
}).pipe(
|
}).pipe(
|
||||||
MutationForm.run,
|
MutationForm.thenRun,
|
||||||
)
|
)
|
||||||
|
|
||||||
const emailField = Form.focusObjectOn(form, "email")
|
const emailField = Form.focusObjectOn(form, "email")
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ const UserProfileEditorView = Component.make("UserProfileEditorView")(function*(
|
|||||||
password: "",
|
password: "",
|
||||||
},
|
},
|
||||||
}).pipe(
|
}).pipe(
|
||||||
LensForm.run,
|
LensForm.thenRun,
|
||||||
)
|
)
|
||||||
|
|
||||||
const emailField = Form.focusObjectOn(form, "email")
|
const emailField = Form.focusObjectOn(form, "email")
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ const QueryRouteComponent = Component.make("QueryRouteView")(function*() {
|
|||||||
),
|
),
|
||||||
staleTime: "10 seconds",
|
staleTime: "10 seconds",
|
||||||
}).pipe(
|
}).pipe(
|
||||||
Query.run,
|
Query.thenRun,
|
||||||
)
|
)
|
||||||
|
|
||||||
const mutation = yield* Mutation.make({
|
const mutation = yield* Mutation.make({
|
||||||
|
|||||||
Reference in New Issue
Block a user