ccstate 4.0.0 → 4.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -9,17 +9,16 @@
9
9
  [![CI](https://github.com/e7h4n/ccstate/actions/workflows/ci.yaml/badge.svg)](https://github.com/e7h4n/ccstate/actions/workflows/ci.yaml)
10
10
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
11
11
 
12
- CCState is a semantic, strict, and flexible state management library suitable for medium to large single-page applications with complex state management needs.
12
+ CCState is a modern signals-based state management library that elegantly implements async computed and read-write capability isolation based on signal features, making it suitable for medium to large web applications.
13
13
 
14
- The name of CCState comes from three basic data types: computed, command, and state.
14
+ The name of CCState comes from three basic types: `Computed`, `Command`, and `State`.
15
15
 
16
16
  ## Quick Features
17
17
 
18
- - ðŸ’Ŋ Simple & Intuitive: Crystal-clear API design with just 3 data types and 2 operations
18
+ - ✈ïļ Intuitive async computation: using async/await or try/catch to process async flow as regular JavaScript without any additional concept
19
+ - ðŸ’Ŋ Simple & Intuitive: Crystal-clear API design with just three types and two operations
19
20
  - ✅ Rock-solid Reliability: Comprehensive test coverage reaching 100% branch coverage
20
- - ðŸŠķ Ultra-lightweight: Zero dependencies, only 500 lines of core code
21
21
  - ðŸ’Ą Framework Agnostic: Seamlessly works with [React](docs/react.md), [Vue](docs/vue.md), [Solid.js](docs/solid.md), [Vanilla](docs/vanilla.md), or any UI framework
22
- - 🚀 Blazing Fast: Optimized performance from day one, 2x-7x faster than Jotai across scenarios
23
22
 
24
23
  ## Getting Started
25
24
 
@@ -36,16 +35,18 @@ pnpm add ccstate
36
35
  yarn add ccstate
37
36
  ```
38
37
 
39
- ### Create Data
38
+ ### Create Signals
40
39
 
41
40
  Use `state` to store a simple value unit, and use `computed` to create a derived computation logic:
42
41
 
43
42
  ```ts
44
- // data.js
43
+ // signals.js
45
44
  import { state, computed } from 'ccstate';
46
45
 
46
+ // a simple value unit which supports read/write
47
47
  export const userId$ = state('');
48
48
 
49
+ // intuitive async computation logic
49
50
  export const user$ = computed(async (get) => {
50
51
  const userId = get(userId$);
51
52
  if (!userId) return null;
@@ -55,14 +56,14 @@ export const user$ = computed(async (get) => {
55
56
  });
56
57
  ```
57
58
 
58
- ### Use data in React
59
+ ### Use signals in React
59
60
 
60
- Use `useGet` and `useSet` hooks in React to get/set data, and use `useResolved` to get Promise value.
61
+ Use `useGet` and `useSet` hooks in React to get/set signals, and use `useResolved` to get Promise value.
61
62
 
62
63
  ```jsx
63
- // App.js
64
+ // App.jsx
64
65
  import { useGet, useSet, useResolved } from 'ccstate-react';
65
- import { userId$, user$ } from './data';
66
+ import { userId$, user$ } from './signals';
66
67
 
67
68
  export default function App() {
68
69
  const userId = useGet(userId$);
@@ -84,18 +85,9 @@ export default function App() {
84
85
  </div>
85
86
  );
86
87
  }
87
-
88
- // main.jsx
89
- import { createRoot } from 'react-dom/client';
90
- import App from './App';
91
-
92
- const rootElement = document.getElementById('root');
93
- const root = createRoot(rootElement);
94
-
95
- root.render(<App />);
96
88
  ```
97
89
 
98
- That's it! [Click here to see the full example](https://codesandbox.io/p/sandbox/cr3xg6).
90
+ That's it! [Click here to see the full example](https://stackblitz.com/edit/vitejs-vite-rert8c7a?file=src%2FApp.tsx).
99
91
 
100
92
  Through these examples, you should have understood the basic usage of CCState. Next, you can read to learn about CCState's core APIs.
101
93
 
@@ -121,13 +113,14 @@ const user$ = state<({
121
113
  name: 'e7h4n',
122
114
  avatar: 'https://avatars.githubusercontent.com/u/813596',
123
115
  } | undefined>(undefined);
116
+
124
117
  store.set({
125
118
  name: 'yc-kanyun',
126
119
  avatar: 'https://avatars.githubusercontent.com/u/168416598'
127
120
  });
128
121
  ```
129
122
 
130
- These examples should be very easy to understand. You might notice a detail in the examples: all variables returned by `state` have a `$` suffix. This is a naming convention used to distinguish an CCState data type from other regular types. CCState data types must be accessed through the store's get/set methods, and since it's common to convert an CCState data type to a regular type using get, the `$` suffix helps avoid naming conflicts.
123
+ These examples should be very easy to understand. You might notice a detail in the examples: all variables returned by `state` have a `$` suffix. This is a naming convention used to distinguish an CCState signal type from other regular types. CCState signal types must be accessed through the store's get/set methods, and since it's common to convert an CCState signal type to a regular type using get, the `$` suffix helps avoid naming conflicts.
131
124
 
132
125
  ### Store
133
126
 
@@ -143,7 +136,7 @@ const otherStore = createStore(); // another new Map()
143
136
  otherStore.get(count$); // anotherMap[$count] ?? $count.init, returns 0
144
137
  ```
145
138
 
146
- This should be easy to understand. If `Store` only needed to support `State` types, a simple Map would be sufficient. However, CCState needs to support two additional data types. Next, let's introduce `Computed`, CCState's reactive computation unit.
139
+ This should be easy to understand. If `Store` only needed to support `State` types, a simple Map would be sufficient. However, CCState needs to support two additional signal types. Next, let's introduce `Computed`, CCState's reactive computation unit.
147
140
 
148
141
  ### Computed
149
142
 
@@ -168,13 +161,13 @@ Does this example seem less intuitive than `State`? Here's a mental model that m
168
161
  - `computed(fn)` returns an object `{read: fn}`, which is assigned to `user$`
169
162
  - When `store.get(user$)` encounters an object which has a read function, it calls that function: `user$.read(store.get)`
170
163
 
171
- This way, `Computed` receives a get accessor that can access other data in the store. This get accessor is similar to `store.get` and can be used to read both `State` and `Computed`. The reason CCState specifically passes a get method to `Computed`, rather than allowing direct access to the store within `Computed`, is to shield the logic within `Computed` from other store methods like `store.set`. The key characteristic of `Computed` is that it can only read states from the store but cannot modify them. In other words, `Computed` is side-effect free.
164
+ This way, `Computed` receives a get accessor that can access other signal in the store. This get accessor is similar to `store.get` and can be used to read both `State` and `Computed`. The reason CCState specifically passes a get method to `Computed`, rather than allowing direct access to the store within `Computed`, is to shield the logic within `Computed` from other store methods like `store.set`. The key characteristic of `Computed` is that it can only read states from the store but cannot modify them. In other words, `Computed` is side-effect free.
172
165
 
173
166
  In most cases, side-effect free computation logic is extremely useful. They can be executed any number of times and have few requirements regarding execution timing. `Computed` is one of the most powerful features in CCState, and you should try to write your logic as `Computed` whenever possible, unless you need to perform set operations on the `Store`.
174
167
 
175
168
  ### Command
176
169
 
177
- `Command` is CCState's logic unit for organizing side effects. It has both `set` and `get` accessors from the store, allowing it to not only read other data types but also modify `State` or call other `Command`.
170
+ `Command` is CCState's logic unit for organizing side effects. It has both `set` and `get` accessors from the store, allowing it to not only read other signal types but also modify `State` or call other `Command`.
178
171
 
179
172
  ```typescript
180
173
  import { command, createStore } from 'ccstate';
@@ -332,9 +325,9 @@ While Jotai is a great state management solution that has benefited the Motiff p
332
325
  - Should reduce reactive capabilities, especially the `onMount` capability - the framework shouldn't provide this ability
333
326
  - Some implicit magic operations, especially Promise wrapping, make the application execution process less transparent
334
327
 
335
- To address these issues, I got an idea: "What concepts in Jotai are essential? And which concepts create mental overhead for developers?". Rather than just discussing it theoretically, I decided to try implementing it myself. So I created CCState to express my thoughts on state management. Before detailing the differences from Jotai, we need to understand CCState's data types and subscription system.
328
+ To address these issues, I got an idea: "What concepts in Jotai are essential? And which concepts create mental overhead for developers?". Rather than just discussing it theoretically, I decided to try implementing it myself. So I created CCState to express my thoughts on state management. Before detailing the differences from Jotai, we need to understand CCState's signal types and subscription system.
336
329
 
337
- ### More semantic data types
330
+ ### More semantic atom types
338
331
 
339
332
  Like Jotai, CCState is also an Atom State solution. However, unlike Jotai, CCState doesn't expose Raw Atom, instead dividing Atoms into three types:
340
333
 
@@ -355,7 +348,7 @@ export const userIdChange$ = command(({ get, set }) => {
355
348
  });
356
349
 
357
350
  // ...
358
- import { userId$, userIdChange$ } from './data';
351
+ import { userId$, userIdChange$ } from './signals';
359
352
 
360
353
  function setupPage() {
361
354
  const store = createStore();
@@ -436,7 +429,7 @@ export function App() {
436
429
  When designing CCState, we wanted the trigger points for value changes to be completely detached from React's Mount/Unmount lifecycle and completely decoupled from React's rendering behavior.
437
430
 
438
431
  ```jsx
439
- // data.js
432
+ // signals.js
440
433
  export const userId$ = state(0)
441
434
  export const init$ = command(({set}) => {
442
435
  const userId = // ... parse userId from location search