repos / starfx

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

commit
bade988
parent
351a8d3
author
Eric Bower
date
2024-08-17 01:10:15 +0000 UTC
docs: copy
6 files changed,  +139, -18
M docs/main.go
+10, -0
 1@@ -45,6 +45,11 @@ func main() {
 2 						Href: "/endpoints",
 3 						Page: pager("endpoints.md"),
 4 					},
 5+					{
 6+						Text: "Dispatch",
 7+						Href: "/dispatch",
 8+						Page: pager("dispatch.md"),
 9+					},
10 				},
11 			},
12 			{
13@@ -63,6 +68,11 @@ func main() {
14 						Href: "/schema",
15 						Page: pager("schema.md"),
16 					},
17+					{
18+						Text: "Selectors",
19+						Href: "/selectors",
20+						Page: pager("selectors.md"),
21+					},
22 				},
23 			},
24 			{
A docs/posts/dispatch.md
+62, -0
 1@@ -0,0 +1,62 @@
 2+---
 3+title: Dispatch
 4+description: How to activate controllers
 5+---
 6+
 7+We use the term `dispatch` when we are emitting an event with a specific type
 8+signature
 9+([flux standard action](https://github.com/redux-utilities/flux-standard-action)).
10+
11+There are two ways to activate a thunk: by dispatching an action or calling it
12+within another thunk.
13+
14+The type signature of `dispatch`:
15+
16+```ts
17+type Dispatch = (a: Action | Action[]) => any;
18+```
19+
20+Within `starfx`, the `dispatch` function lives on the store.
21+
22+```ts
23+const { createSchema, createStore } from "starfx";
24+const [schema, initialState] = createSchema();
25+const store = createStore({ initialState });
26+
27+store.dispatch({ type: "action", payload: {} });
28+```
29+
30+You can also use dispatch with a `react` hook:
31+
32+```tsx
33+import { useDispatch } from "starfx/react";
34+
35+function App() {
36+  const dispatch = useDispatch();
37+
38+  return <button onClick={() => dispatch({ type: "click" })}>Click me!</button>;
39+}
40+```
41+
42+# Listening to actions
43+
44+This is a pubsub system after all. How can we listen to action events?
45+
46+```ts
47+import { take } from "starfx";
48+
49+function* watch() {
50+  while (true) {
51+    const action = yield* take("click");
52+    // -or- const action = yield* take("*");
53+    // -or- const action = yield* take((act) => act.type === "click");
54+    // -or- const action = yield* take(["click", "me"]);
55+    console.log(action.payload);
56+  }
57+}
58+
59+store.run(watch);
60+```
61+
62+`watch` is what we call a [supervisor](/supervisors). Click that link to learn
63+more about how they provide powerful flow control mechanisms.
M docs/posts/learn.md
+22, -16
 1@@ -30,22 +30,28 @@ It all happens as a single unidirectional loop.
 2 
 3 `starfx` is different in a number of ways.
 4 
 5-First, we combine both state and side-effect management into a single cohesive
 6-unit. This streamlines the implementation of your web app.
 7-
 8-Second, business logic does not live inside of `react`, rather, it lives inside
 9-of the side-effect system. We are not shackled by `react` lifecycle hooks, in
10-fact, `starfx` has virtually no concept of `react` at all -- except for a couple
11-of hooks. Then entire system is designed, from the ground up, to not need
12-`react` at all in order to function. At the end of the day, `starfx` works by
13-subscribing to and publishing events. Those events could come from `react`, but
14-they could also come from anywhere.
15-
16-Thirdly, we have taken the best part about `express` and `koa` and applied it to
17-fetching API data on the front-end. What this means is that we have a powerful
18-middleware system that we can leverage on the front-end.
19-
20-# Why does `starfx` use js generators?
21+We combine both state and side-effect management into a single cohesive unit.
22+This streamlines the implementation of your web app.
23+
24+Our business logic does not live inside of `react`, rather, it lives inside of
25+the side-effect system. We are not shackled by `react` lifecycle hooks, in fact,
26+`starfx` has virtually no concept of `react` at all -- except for a couple of
27+hooks. The entire system is designed, from the ground up, to not need `react` at
28+all in order to function. At the end of the day, `starfx` works by subscribing
29+to and publishing events. Those events could come from `react`, but they could
30+also come from anywhere.
31+
32+We have taken the best part about `express` and `koa` and applied it to fetching
33+API data on the front-end. What this means is that we have a powerful middleware
34+system that we can leverage on the front-end.
35+
36+We built a state management system leveraging the concept of a database schema.
37+We took inspiration from [zod](https://zod.dev) to build an ergonomic and
38+powerful state system leveraging reusable slice helpers. With our schema and
39+custom built store, we can replace all of boilerplate with a single function
40+call `createSchema()`.
41+
42+# Why does `starfx` use [generators](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Generator)?
43 
44 Generators give us -- the library authors -- more control over how side-effects
45 are handled within a javascript runtime environment. There are things that we
A docs/posts/selectors.md
+41, -0
 1@@ -0,0 +1,41 @@
 2+---
 3+title: Selectors
 4+Description: Deriving data with selectors 
 5+---
 6+
 7+In a typical web app, the logic for deriving data is usually written as
 8+functions we call selectors.
 9+
10+The basic function signature of a selector:
11+
12+```ts
13+const selectData = (state: WebState) => state.data;
14+```
15+
16+Selectors are primarily used to encapsulate logic for looking up specific values
17+from state, logic for actually deriving values, and improving performance by
18+avoiding unnecessary recalculations.
19+
20+To learn more, redux has excellent docs
21+[on deriving data with selectors](https://redux.js.org/usage/deriving-data-selectors).
22+
23+There is 100% knowledge transfer between selectors in `starfx` and `redux`
24+because we adhere to the same function signature.
25+
26+The only difference is that as part of our API we re-export
27+[reselect.createSelector](https://reselect.js.org/api/createselector/), which
28+will memoize functions:
29+
30+```ts
31+import { createSelector } from "starfx";
32+
33+const selectData = (state) => state.data;
34+const myselector = createSelector(
35+  selectData,
36+  (data) => data.sort((a, b) => a.id - b.id);
37+);
38+```
39+
40+Function memoization is just a way to cache a function call. If the dependencies
41+(e.g. the result of `selectData`) don't change, then `myselector` will not be
42+called: it will return its previous value.
M docs/posts/supervisors.md
+3, -1
 1@@ -6,7 +6,7 @@ description: Learn how supervisor tasks work
 2 A supervisor task is a way to monitor children tasks and probably most
 3 importantly, manage their health. By structuring your side-effects and business
 4 logic around supervisor tasks, we gain very interesting coding paradigms that
 5-result is easier to read and manage code.
 6+result in easier to read and manage code.
 7 
 8 [Supplemental reading from erlang](https://www.erlang.org/doc/design_principles/des_princ)
 9 
10@@ -14,6 +14,8 @@ The most basic version of a supervisor is simply an infinite loop that calls a
11 child task:
12 
13 ```ts
14+import { call } from "starfx";
15+
16 function* supervisor() {
17   while (true) {
18     try {
M store/types.ts
+1, -1
1@@ -50,7 +50,7 @@ export interface FxStore<S extends AnyState> {
2   reset: (ignoreList?: (keyof S)[]) => Operation<UpdaterCtx<S>>;
3   run: ReturnType<typeof createRun>;
4   // deno-lint-ignore no-explicit-any
5-  dispatch: (a: AnyAction) => any;
6+  dispatch: (a: AnyAction | AnyAction[]) => any;
7   replaceReducer: (r: (s: S, a: AnyAction) => S) => void;
8   getInitialState: () => S;
9   // deno-lint-ignore no-explicit-any