diff --git a/packages/example/src/routes/todos.tsx b/packages/example/src/routes/todos.tsx
index 17aa88f..89591a8 100644
--- a/packages/example/src/routes/todos.tsx
+++ b/packages/example/src/routes/todos.tsx
@@ -26,9 +26,9 @@ function Todos() {
return (
-
+
-
+
)
diff --git a/packages/example/src/todos/services/TodosState.ts b/packages/example/src/todos/services/TodosState.ts
index d9b2bea..a3735c8 100644
--- a/packages/example/src/todos/services/TodosState.ts
+++ b/packages/example/src/todos/services/TodosState.ts
@@ -2,14 +2,14 @@ import { Todo } from "@/domain"
import { KeyValueStore } from "@effect/platform"
import { BrowserKeyValueStore } from "@effect/platform-browser"
import { PlatformError } from "@effect/platform/Error"
-import { Chunk, Context, Effect, identity, Layer, ParseResult, Ref, Schema, SubscriptionRef } from "effect"
+import { Chunk, Context, Effect, identity, Layer, ParseResult, Ref, Schema, Stream, SubscriptionRef } from "effect"
export class TodosState extends Context.Tag("TodosState")>
- readonly readFromLocalStorage: Effect.Effect
- readonly saveToLocalStorage: Effect.Effect
+ readonly load: Effect.Effect
+ readonly save: Effect.Effect
readonly prepend: (todo: Todo.Todo) => Effect.Effect
readonly replace: (index: number, todo: Todo.Todo) => Effect.Effect
@@ -20,34 +20,31 @@ export class TodosState extends Context.Tag("TodosState") Layer.effect(TodosState, Effect.gen(function*() {
- const todos = yield* SubscriptionRef.make(Chunk.empty())
-
const readFromLocalStorage = KeyValueStore.KeyValueStore.pipe(
Effect.flatMap(kv => kv.get(key)),
Effect.flatMap(identity),
- Effect.flatMap(Schema.parseJson().pipe(
- Schema.compose(Schema.Chunk(Todo.TodoFromJson)),
- Schema.decode,
+ Effect.flatMap(Schema.decode(
+ Schema.compose(Schema.parseJson(), Schema.Chunk(Todo.TodoFromJson))
)),
- Effect.flatMap(v => Ref.set(todos, v)),
-
- Effect.catchTag("NoSuchElementException", () => Ref.set(todos, Chunk.empty())),
-
+ Effect.catchTag("NoSuchElementException", () => Effect.succeed(Chunk.empty())),
Effect.provide(BrowserKeyValueStore.layerLocalStorage),
)
- const saveToLocalStorage = Effect.all([KeyValueStore.KeyValueStore, todos]).pipe(
- Effect.flatMap(([kv, values]) => values.pipe(
- Schema.parseJson().pipe(
- Schema.compose(Schema.Chunk(Todo.TodoFromJson)),
- Schema.encode,
+ const writeToLocalStorage = (values: Chunk.Chunk) => KeyValueStore.KeyValueStore.pipe(
+ Effect.flatMap(kv => values.pipe(
+ Schema.encode(
+ Schema.compose(Schema.parseJson(), Schema.Chunk(Todo.TodoFromJson))
),
Effect.flatMap(v => kv.set(key, v)),
)),
-
Effect.provide(BrowserKeyValueStore.layerLocalStorage),
)
+ const todos = yield* SubscriptionRef.make(yield* readFromLocalStorage)
+
+ const load = Effect.flatMap(readFromLocalStorage, v => Ref.set(todos, v))
+ const save = Effect.flatMap(todos, writeToLocalStorage)
+
const prepend = (todo: Todo.Todo) => Ref.update(todos, Chunk.prepend(todo))
const replace = (index: number, todo: Todo.Todo) => Ref.update(todos, Chunk.replace(index, todo))
const remove = (index: number) => Ref.update(todos, Chunk.remove(index))
@@ -56,12 +53,16 @@ export const make = (key: string) => Layer.effect(TodosState, Effect.gen(functio
// })
- yield* readFromLocalStorage
+ // Sync changes with local storage
+ yield* Effect.forkScoped(todos.changes.pipe(
+ Stream.debounce("500 millis"),
+ Stream.runForEach(writeToLocalStorage),
+ ))
return {
todos,
- readFromLocalStorage,
- saveToLocalStorage,
+ load,
+ save,
prepend,
replace,
remove,
diff --git a/packages/example/src/todos/views/VNewTodo.tsx b/packages/example/src/todos/views/VNewTodo.tsx
index 4d183b5..c090df6 100644
--- a/packages/example/src/todos/views/VNewTodo.tsx
+++ b/packages/example/src/todos/views/VNewTodo.tsx
@@ -1,25 +1,27 @@
import { Todo } from "@/domain"
import { Box, Button, Card, Flex, TextArea } from "@radix-ui/themes"
-import { Effect, Option, SubscriptionRef } from "effect"
+import { Effect, Option, Ref } from "effect"
import { R } from "../reffuse"
import { TodosState } from "../services"
-const createEmptyTodo = Todo.generateUniqueID.pipe(
- Effect.map(id => Todo.Todo.make({
- id,
- content: "",
- completedAt: Option.none(),
- }, true))
-)
+const createEmptyTodo = Effect.map(Todo.generateUniqueID, id => Todo.Todo.make({
+ id,
+ content: "",
+ completedAt: Option.none(),
+}, true))
export function VNewTodo() {
- const runSync = R.useRunSync()
+ const todoRef = R.useRef(() => createEmptyTodo)
+ const [content, setContent] = R.useRefState(R.useSubRef(todoRef, ["content"]))
- const todoRef = R.useMemo(() => createEmptyTodo.pipe(Effect.flatMap(SubscriptionRef.make)), [])
- const [todo, setTodo] = R.useRefState(todoRef)
+ const add = R.useCallbackSync(() => Effect.all([TodosState.TodosState, todoRef]).pipe(
+ Effect.flatMap(([state, todo]) => state.prepend(todo)),
+ Effect.andThen(createEmptyTodo),
+ Effect.flatMap(v => Ref.set(todoRef, v)),
+ ), [todoRef])
return (
@@ -27,23 +29,12 @@ export function VNewTodo() {
diff --git a/packages/example/src/todos/views/VTodos.tsx b/packages/example/src/todos/views/VTodos.tsx
index 013fa3e..db3166d 100644
--- a/packages/example/src/todos/views/VTodos.tsx
+++ b/packages/example/src/todos/views/VTodos.tsx
@@ -1,5 +1,5 @@
import { Box, Flex } from "@radix-ui/themes"
-import { Chunk, Effect, Stream } from "effect"
+import { Chunk, Effect } from "effect"
import { R } from "../reffuse"
import { TodosState } from "../services"
import { VNewTodo } from "./VNewTodo"
@@ -8,13 +8,6 @@ import { VTodo } from "./VTodo"
export function VTodos() {
- // Sync changes to the todos with the local storage
- R.useFork(() => TodosState.TodosState.pipe(
- Effect.flatMap(state =>
- Stream.runForEach(state.todos.changes, () => state.saveToLocalStorage)
- )
- ), [])
-
const todosRef = R.useMemo(() => TodosState.TodosState.pipe(Effect.map(state => state.todos)), [])
const [todos] = R.useSubscribeRefs(todosRef)