repos / starfx

supercharged async flow control library.
git clone https://github.com/neurosnap/starfx.git

commit
60440e9
parent
fb0b016
author
Eric Bower
date
2023-12-18 18:59:28 +0000 UTC
docs: readme
1 files changed,  +50, -9
M README.md
+50, -9
  1@@ -42,13 +42,13 @@ As I've been developing these specialized thunks, I'm starting to think of them
  2 more like micro-controllers. Only thunks and endpoints have the ability to
  3 update state. However, thunks are not tied to any particular view and in that
  4 way are more composable. Thunks can call other thunks and you have the async
  5-flow control tools from effection to faciliate coordination.
  6+flow control tools from effection to facilitate coordination.
  7 
  8 Every thunk that is created requires a unique id -- user provided string. This
  9 provides us with a handful of benefits:
 10 
 11 - User hand-labels each thunk created
 12-- Better tracability (via labels)
 13+- Better traceability (via labels)
 14 - Easier to debug async and side-effects in general (via labels)
 15 - Build abstractions off naming conventions (e.g. creating routers
 16   `/users [GET]`)
 17@@ -74,15 +74,19 @@ thunks.use(function* (ctx, next) {
 18 });
 19 
 20 // create a thunk
 21-const log = thunks.create("log", function* (ctx, next) {
 22-  const resp = yield* call(fetch("https://bower.sh"));
 23-  const data = yield* call(resp.json());
 24+const log = thunks.create<string>("log", function* (ctx, next) {
 25+  const resp = yield* call(
 26+    fetch("https://log-drain.com", {
 27+      method: "POST",
 28+      body: JSON.stringify({ message: ctx.payload }),
 29+    }),
 30+  );
 31   console.log("before calling next middleware");
 32   yield* next();
 33   console.log("after all remaining middleware have run");
 34 });
 35 
 36-store.dispatch(log());
 37+store.dispatch(log("sending log message"));
 38 // output:
 39 // before calling next middleware
 40 // last mdw in the stack
 41@@ -112,6 +116,42 @@ store.dispatch(fetchUsers());
 42 
 43 # example: an immutable store that acts like a reactive, in-memory database
 44 
 45+I love `redux`. I know it gets sniped for having too much boilerplate when
 46+alternatives like `zustand` and `react-query` exist that cut through the
 47+ceremony of managing state. However, `redux` was never designed to be easy to
 48+use, it was designed to be scalable, debuggable, and maintainable. Yes, setting
 49+up a `redux` store is work, but that is in an effort to serve its
 50+maintainability.
 51+
 52+Having said that, the core abstraction in `redux` is a reducer. Reducers were
 53+originally designed to contain isolated business logic of updating sections of
 54+state (also known as state slices). They were also designed to make it easier to
 55+sustain state immutability.
 56+
 57+Fast forward to `redux-toolkit` and we have `createSlice` which leverages
 58+`immer` under-the-hood to ensure immutability. So we no longer need reducers for
 59+immutability.
 60+
 61+Further, I argue, placing the business logic for updating state inside reducers
 62+(via switch-cases) makes understanding business logic harder. Instead of having
 63+a single function that updates X state slices, we have X functions (reducers)
 64+that we need to piece together in our heads to understand what is being updated
 65+when an action is dispatched.
 66+
 67+Therefore, reducers are not great containers for business logic. They are rigid
 68+and require the end-developer to piece them together in their head to fully
 69+understand the ramifications of dispatching an action.
 70+
 71+With all of this in mind, `starfx/store` takes all the good parts of `redux` and
 72+removes the need for reducers entirely. We still have a single state object that
 73+contains everything from data fetched from an API, UX, and a way to create
 74+memoized functions (e.g. selectors). We maintain immutability (using `immer`)
 75+and also have a middleware system to extend it.
 76+
 77+Finally, we bring the utility of creating a schema (like `zod` or a traditional
 78+database) to make it plainly obvious what the state shape looks like as well as
 79+reusable utilities to make it easy to update and query state.
 80+
 81 ```ts
 82 import { configureStore, createSchema, select, slice } from "starfx/store";
 83 
 84@@ -127,6 +167,7 @@ const { db, initialState, update } = createSchema({
 85   loaders: slice.loader(),
 86 });
 87 
 88+// just a normal endpoint
 89 const fetchUsers = api.get<never, User[]>(
 90   "/users",
 91   function* (ctx, next) {
 92@@ -146,7 +187,7 @@ const fetchUsers = api.get<never, User[]>(
 93     }, {});
 94 
 95     // update the store and trigger a re-render in react
 96-    yield* update(db.users.add(users));
 97+    yield* schema.update(db.users.add(users));
 98 
 99     // User[]
100     const users = yield* select(db.users.selectTableAsList);
101@@ -308,10 +349,10 @@ That's it. We are just leveraging the same tiny API that we are already using in
102 
103 # talk
104 
105-I recently gave a talk about deliminited continuations where I also discuss this
106+I recently gave a talk about delimited continuations where I also discuss this
107 library:
108 
109-[![Delminited continuations are all you need](http://img.youtube.com/vi/uRbqLGj_6mI/0.jpg)](https://youtu.be/uRbqLGj_6mI?si=Mok0J8Wp0Z-ahFrN)
110+[![Delimited continuations are all you need](http://img.youtube.com/vi/uRbqLGj_6mI/0.jpg)](https://youtu.be/uRbqLGj_6mI?si=Mok0J8Wp0Z-ahFrN)
111 
112 # resources
113