Add Effect v4 support, Fast Refresh tooling, and revamped docs #56

Merged
Thilawyn merged 133 commits from next into master 2026-07-26 02:32:59 +02:00
4 changed files with 18 additions and 78 deletions
Showing only changes of commit 3a25b1a34e - Show all commits
+3 -30
View File
@@ -15,7 +15,6 @@ import { Route as BlankRouteImport } from './routes/blank'
import { Route as FormRouteImport } from './routes/form' import { Route as FormRouteImport } from './routes/form'
import { Route as LensformRouteImport } from './routes/lensform' import { Route as LensformRouteImport } from './routes/lensform'
import { Route as QueryRouteImport } from './routes/query' import { Route as QueryRouteImport } from './routes/query'
import { Route as ResultRouteImport } from './routes/result'
const IndexRoute = IndexRouteImport.update({ const IndexRoute = IndexRouteImport.update({
id: '/', id: '/',
@@ -47,11 +46,6 @@ const QueryRoute = QueryRouteImport.update({
path: '/query', path: '/query',
getParentRoute: () => rootRouteImport, getParentRoute: () => rootRouteImport,
} as any) } as any)
const ResultRoute = ResultRouteImport.update({
id: '/result',
path: '/result',
getParentRoute: () => rootRouteImport,
} as any)
export interface FileRoutesByFullPath { export interface FileRoutesByFullPath {
'/': typeof IndexRoute '/': typeof IndexRoute
@@ -60,7 +54,6 @@ export interface FileRoutesByFullPath {
'/form': typeof FormRoute '/form': typeof FormRoute
'/lensform': typeof LensformRoute '/lensform': typeof LensformRoute
'/query': typeof QueryRoute '/query': typeof QueryRoute
'/result': typeof ResultRoute
} }
export interface FileRoutesByTo { export interface FileRoutesByTo {
'/': typeof IndexRoute '/': typeof IndexRoute
@@ -69,7 +62,6 @@ export interface FileRoutesByTo {
'/form': typeof FormRoute '/form': typeof FormRoute
'/lensform': typeof LensformRoute '/lensform': typeof LensformRoute
'/query': typeof QueryRoute '/query': typeof QueryRoute
'/result': typeof ResultRoute
} }
export interface FileRoutesById { export interface FileRoutesById {
__root__: typeof rootRouteImport __root__: typeof rootRouteImport
@@ -79,23 +71,13 @@ export interface FileRoutesById {
'/form': typeof FormRoute '/form': typeof FormRoute
'/lensform': typeof LensformRoute '/lensform': typeof LensformRoute
'/query': typeof QueryRoute '/query': typeof QueryRoute
'/result': typeof ResultRoute
} }
export interface FileRouteTypes { export interface FileRouteTypes {
fileRoutesByFullPath: FileRoutesByFullPath fileRoutesByFullPath: FileRoutesByFullPath
fullPaths: fullPaths: '/' | '/async' | '/blank' | '/form' | '/lensform' | '/query'
'/' | '/async' | '/blank' | '/form' | '/lensform' | '/query' | '/result'
fileRoutesByTo: FileRoutesByTo fileRoutesByTo: FileRoutesByTo
to: '/' | '/async' | '/blank' | '/form' | '/lensform' | '/query' | '/result' to: '/' | '/async' | '/blank' | '/form' | '/lensform' | '/query'
id: id: '__root__' | '/' | '/async' | '/blank' | '/form' | '/lensform' | '/query'
| '__root__'
| '/'
| '/async'
| '/blank'
| '/form'
| '/lensform'
| '/query'
| '/result'
fileRoutesById: FileRoutesById fileRoutesById: FileRoutesById
} }
export interface RootRouteChildren { export interface RootRouteChildren {
@@ -105,7 +87,6 @@ export interface RootRouteChildren {
FormRoute: typeof FormRoute FormRoute: typeof FormRoute
LensformRoute: typeof LensformRoute LensformRoute: typeof LensformRoute
QueryRoute: typeof QueryRoute QueryRoute: typeof QueryRoute
ResultRoute: typeof ResultRoute
} }
declare module '@tanstack/react-router' { declare module '@tanstack/react-router' {
@@ -152,13 +133,6 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof QueryRouteImport preLoaderRoute: typeof QueryRouteImport
parentRoute: typeof rootRouteImport parentRoute: typeof rootRouteImport
} }
'/result': {
id: '/result'
path: '/result'
fullPath: '/result'
preLoaderRoute: typeof ResultRouteImport
parentRoute: typeof rootRouteImport
}
} }
} }
@@ -169,7 +143,6 @@ const rootRouteChildren: RootRouteChildren = {
FormRoute: FormRoute, FormRoute: FormRoute,
LensformRoute: LensformRoute, LensformRoute: LensformRoute,
QueryRoute: QueryRoute, QueryRoute: QueryRoute,
ResultRoute: ResultRoute,
} }
export const routeTree = rootRouteImport export const routeTree = rootRouteImport
._addFileChildren(rootRouteChildren) ._addFileChildren(rootRouteChildren)
@@ -19,7 +19,6 @@ function Root() {
<Link to="/blank">Blank</Link> <Link to="/blank">Blank</Link>
<Link to="/async">Async</Link> <Link to="/async">Async</Link>
<Link to="/query">Query</Link> <Link to="/query">Query</Link>
<Link to="/result">Result</Link>
<Link to="/form">Form</Link> <Link to="/form">Form</Link>
<Link to="/lensform">LensForm</Link> <Link to="/lensform">LensForm</Link>
</Flex> </Flex>
+15 -2
View File
@@ -1,11 +1,19 @@
import { Container, Flex, Heading, Slider, Text, TextField } from "@radix-ui/themes" import { Container, Flex, Heading, Slider, Text, TextField } from "@radix-ui/themes"
import { createFileRoute } from "@tanstack/react-router" import { createFileRoute } from "@tanstack/react-router"
import { Console, Effect } from "effect"
import { Async, Component, Memoized } from "effect-fc-next" import { Async, Component, Memoized } from "effect-fc-next"
import * as React from "react" import * as React from "react"
import { fetchPost } from "@/post"
import { runtime } from "@/runtime" import { runtime } from "@/runtime"
const fetchPost = (id: number) => Effect.sleep("500 millis").pipe(
Effect.as({
title: `Post ${id}`,
body: `This is the content of post ${id}.`,
}),
)
interface AsyncFetchPostViewProps { interface AsyncFetchPostViewProps {
readonly id: number readonly id: number
} }
@@ -13,6 +21,11 @@ interface AsyncFetchPostViewProps {
const AsyncFetchPostView = Component.make("AsyncFetchPostView")(function*( const AsyncFetchPostView = Component.make("AsyncFetchPostView")(function*(
props: AsyncFetchPostViewProps, props: AsyncFetchPostViewProps,
) { ) {
yield* Component.useOnMount(() => Effect.gen(function*() {
yield* Effect.addFinalizer(() => Console.log("AsyncFetchPostView unmounted"))
yield* Console.log("AsyncFetchPostView mounted")
}))
const post = yield* Component.useOnChange( const post = yield* Component.useOnChange(
() => fetchPost(props.id), () => fetchPost(props.id),
[props.id], [props.id],
@@ -51,7 +64,7 @@ const AsyncRouteComponent = Component.make("AsyncRouteView")(function*() {
onValueChange={([value]) => setId(value ?? 1)} onValueChange={([value]) => setId(value ?? 1)}
/> />
<AsyncFetchPost id={id} fallback={<Text>Loading post...</Text>} /> <AsyncFetchPost id={id} />
</Flex> </Flex>
</Container> </Container>
) )
@@ -1,45 +0,0 @@
import { Button, Container, Flex, Heading, Text } from "@radix-ui/themes"
import { createFileRoute } from "@tanstack/react-router"
import { AsyncResult } from "effect/unstable/reactivity"
import { Component, Mutation, View } from "effect-fc-next"
import { fetchPost, type Post } from "@/post"
import { runtime } from "@/runtime"
const PostResultView = (props: { readonly result: AsyncResult.AsyncResult<Post, Error> }) =>
AsyncResult.match(props.result, {
onInitial: () => <Text>Ready to load.</Text>,
onFailure: () => <Text>Request failed.</Text>,
onSuccess: result => (
<>
<Heading>{result.value.title}</Heading>
<Text>{result.value.body}</Text>
</>
),
})
const ResultRouteComponent = Component.make("ResultRouteView")(function*() {
const mutation = yield* Component.useOnMount(() => Mutation.make({
f: (_: undefined) => fetchPost(1),
}))
const [result] = yield* View.useAll([mutation.state])
const runPromise = yield* Component.useRunPromise()
return (
<Container>
<Flex direction="column" gap="2">
<Button onClick={() => void runPromise(mutation.mutate(undefined))}>
Load post
</Button>
<PostResultView result={result} />
</Flex>
</Container>
)
}).pipe(
Component.withContext(runtime.context),
)
export const Route = createFileRoute("/result")({
component: ResultRouteComponent,
})