@chrrrs/signals 0.1.0 → 0.1.1
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 +139 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,8 +1,117 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Reactive Signals for React
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A tiny, fully typed and zero-dependency signals library for React 19, inspired by SolidJS and Apollo.
|
|
4
|
+
Features include synchronous signals, computed signals with auto-tracking, async signals with Suspense, selector hooks, and batching.
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## ⚡ Features
|
|
9
|
+
|
|
10
|
+
✅ Simple reactive signals (createSignal)
|
|
11
|
+
✅ Auto-tracked computed signals (computed)
|
|
12
|
+
✅ React hooks (useSignal, useSignalSelector)
|
|
13
|
+
✅ [WIP] Async signals with Suspense support (createAsyncSignal, useAsyncSignal)
|
|
14
|
+
✅ Global shared signals (like Apollo cache)
|
|
15
|
+
✅ Fully typed and tree-shakeable
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## **📦 Installation**
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
npm install @chrrrs/signals
|
|
23
|
+
# or
|
|
24
|
+
yarn add @chrrrs/signals
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## **🛠️ Basic Usage**
|
|
30
|
+
|
|
31
|
+
### **Create a Signal**
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
import { createSignal } from "@chrrrs/signals";
|
|
35
|
+
|
|
36
|
+
export const count = createSignal(0);
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### **React Hook**
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
import { useSignal } from "@chrrrs/signals";
|
|
43
|
+
import { count } from "./store";
|
|
44
|
+
|
|
45
|
+
export function Counter() {
|
|
46
|
+
const value = useSignal(count);
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
<div>
|
|
50
|
+
<p>{value}</p>
|
|
51
|
+
<button onClick={() => count.set(value + 1)}>Increment</button>
|
|
52
|
+
</div>
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## **✨ Computed Signals**
|
|
60
|
+
|
|
61
|
+
Auto-tracked dependencies — no manual deps required.
|
|
62
|
+
|
|
63
|
+
```
|
|
64
|
+
import { computed } from "@chrrrs/signals";
|
|
65
|
+
import { count } from "./store";
|
|
66
|
+
|
|
67
|
+
const double = computed(() => count.get() * 2);
|
|
68
|
+
|
|
69
|
+
const doubled = useSignal(double);
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## **🚀 [WIP] Async Signals + Suspense**
|
|
75
|
+
|
|
76
|
+
An async signal is available, work-in-progress on the useAsyncSignal hook and Suspense examples
|
|
77
|
+
|
|
78
|
+
```
|
|
79
|
+
import { createAsyncSignal } from "@chrrrs/signals";
|
|
80
|
+
|
|
81
|
+
export const userSignal = createAsyncSignal(async () => {
|
|
82
|
+
const res = await fetch("/api/user");
|
|
83
|
+
return res.json();
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
<button onClick={() => void userSignal.load()}>Get user</button>
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## **🎯 Selector Hook**
|
|
92
|
+
|
|
93
|
+
Subscribe to part of a signal to optimize re-renders:
|
|
94
|
+
|
|
95
|
+
```
|
|
96
|
+
import { useSignalSelector } from "@chrrrs/signals";
|
|
97
|
+
import { userSignal } from "./store";
|
|
98
|
+
|
|
99
|
+
const userName = useSignalSelector(userSignal, user => user?.name ?? "Guest");
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## **✅ Best Practices**
|
|
105
|
+
|
|
106
|
+
1. Immutable values: Always .set() new values; do not mutate objects inside a signal.
|
|
107
|
+
2. Use signals for shared state: Component-local UI state is fine in useState.
|
|
108
|
+
3. Use async signals for initial fetch: Combine with Suspense, but don’t wrap frequently changing data.
|
|
109
|
+
4. Batch updates: Use batch() when updating multiple signals together.
|
|
110
|
+
5. Selector hook: Use useSignalSelector to minimize unnecessary re-renders when only part of the signal matters.
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## **SSR note**
|
|
6
115
|
|
|
7
116
|
Signals created at module scope are shared across SSR requests.
|
|
8
117
|
|
|
@@ -23,3 +132,30 @@ export function createState() {
|
|
|
23
132
|
};
|
|
24
133
|
}
|
|
25
134
|
```
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## **How It Differs From Larger Libraries:**
|
|
139
|
+
|
|
140
|
+
| Feature | `@chrrrs/signals` | Zustand / Jotai / Valtio |
|
|
141
|
+
| ----------------------------- | ------------------------------------------------- | -------------------------------------------------------- |
|
|
142
|
+
| Size & simplicity | Minimal, easy-to-read API | Larger, more concepts to learn |
|
|
143
|
+
| Auto-tracked computed signals | ✅ Recomputes automatically based on dependencies | Zustand: manual derived state Jotai: manual dependencies |
|
|
144
|
+
| Async + Suspense integration | ✅ Built-in support via `createAsyncSignal` | Jotai: supports async atoms, requires extra boilerplate |
|
|
145
|
+
| Batching | ✅ Out-of-the-box | Zustand: requires middleware or manual batching |
|
|
146
|
+
| Tree-shakeable | ✅ Fully modular | Varies |
|
|
147
|
+
| Learning / debugging | ✅ Small, readable codebase | Larger codebases, more abstractions |
|
|
148
|
+
|
|
149
|
+
> **Intended audience:** Developers who want **small, reactive primitives,** not a full-blown state management framework.
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## **Common questions and answers**
|
|
154
|
+
|
|
155
|
+
### **Why not just use useState and context?**
|
|
156
|
+
|
|
157
|
+
useState and context are great for component-local state. If you only need to store a single value, use useState. However, context will cause re-renders of all components that use the context, which can be wasteful, if multiple values or complex state logic is needed, so use signals for shared state instead.
|
|
158
|
+
|
|
159
|
+
### **Why no batching logic?**
|
|
160
|
+
|
|
161
|
+
Batching is implicit in React 19. When you update a signal, all components that use that signal will re-render. No need for a batching function.
|