reactjs-signal 1.0.0 → 1.0.2
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 +382 -1
- package/{src/index.ts → dist/index.d.ts} +22 -170
- package/dist/index.js +1 -0
- package/package.json +14 -2
- package/.github/commit-convention.md +0 -95
- package/.github/workflows/main.yml +0 -21
- package/.github/workflows/release.yml +0 -29
- package/tsconfig.json +0 -26
- package/tsup.config.ts +0 -12
package/README.md
CHANGED
|
@@ -1 +1,382 @@
|
|
|
1
|
-
|
|
1
|
+
<p align="center">
|
|
2
|
+
<a href="https://www.npmjs.com/package/reactjs-signal" target="_blank" rel="noopener noreferrer">
|
|
3
|
+
<img src="https://api.iconify.design/uil:comment-verify.svg?color=%23b3ff75" alt="logo" width='100'/></a>
|
|
4
|
+
</p>
|
|
5
|
+
|
|
6
|
+
<p align="center">
|
|
7
|
+
Share Store State with Signal Pattern
|
|
8
|
+
</p>
|
|
9
|
+
|
|
10
|
+
<p align="center">
|
|
11
|
+
<a href="https://www.npmjs.com/package/reactjs-signal" target="_blank" rel="noopener noreferrer"><img src="https://badge.fury.io/js/reactjs-signal.svg" alt="NPM Version" /></a>
|
|
12
|
+
<a href="https://www.npmjs.com/package/reactjs-signal" target="_blank" rel="noopener noreferrer"><img src="https://img.shields.io/npm/dt/reactjs-signal.svg?logo=npm" alt="NPM Downloads" /></a>
|
|
13
|
+
<a href="https://bundlephobia.com/result?p=reactjs-signal" target="_blank" rel="noopener noreferrer"><img src="https://img.shields.io/bundlephobia/minzip/reactjs-signal" alt="Minizip" /></a>
|
|
14
|
+
<a href="https://github.com/hunghg255/reactjs-signal/graphs/contributors" target="_blank" rel="noopener noreferrer"><img src="https://img.shields.io/badge/all_contributors-1-orange.svg" alt="Contributors" /></a>
|
|
15
|
+
<a href="https://github.com/hunghg255/reactjs-signal/blob/main/LICENSE" target="_blank" rel="noopener noreferrer"><img src="https://badgen.net/github/license/hunghg255/reactjs-signal" alt="License" /></a>
|
|
16
|
+
</p>
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm install reactjs-signal
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
## Usage
|
|
26
|
+
|
|
27
|
+
```tsx
|
|
28
|
+
import React from 'react';
|
|
29
|
+
import { useSignal } from 'reactjs-signal';
|
|
30
|
+
|
|
31
|
+
const App = () => {
|
|
32
|
+
const [state, setState] = useSignal({ count: 0 });
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<div>
|
|
36
|
+
<h1>{state.count}</h1>
|
|
37
|
+
<button onClick={() => setState({ count: state.count + 1 })}>Increment</button>
|
|
38
|
+
</div>
|
|
39
|
+
);
|
|
40
|
+
};
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## API Documentation
|
|
44
|
+
|
|
45
|
+
### `createSignal`
|
|
46
|
+
|
|
47
|
+
Creates a writable Alien Signal.
|
|
48
|
+
|
|
49
|
+
#### Example
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
const countSignal = createSignal(0);
|
|
53
|
+
countSignal.set(10); // sets the value to 10
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
#### Parameters
|
|
57
|
+
|
|
58
|
+
- `initialValue` (`T`): The initial value of the signal.
|
|
59
|
+
|
|
60
|
+
#### Returns
|
|
61
|
+
|
|
62
|
+
- `IWritableSignal<T>`: The created Alien Signal.
|
|
63
|
+
|
|
64
|
+
### `createComputed`
|
|
65
|
+
|
|
66
|
+
Creates a computed Alien Signal based on a getter function.
|
|
67
|
+
|
|
68
|
+
#### Example
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
const countSignal = createSignal(1);
|
|
72
|
+
const doubleSignal = createComputed(() => countSignal.get() * 2);
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
#### Parameters
|
|
76
|
+
|
|
77
|
+
- `fn` (`() => T`): A getter function returning a computed value.
|
|
78
|
+
|
|
79
|
+
#### Returns
|
|
80
|
+
|
|
81
|
+
- `ISignal<T>`: The created computed signal.
|
|
82
|
+
|
|
83
|
+
### `createEffect`
|
|
84
|
+
|
|
85
|
+
Creates a side effect in Alien Signals.
|
|
86
|
+
|
|
87
|
+
#### Example
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
const countSignal = createSignal(1);
|
|
91
|
+
createEffect(() => {
|
|
92
|
+
console.log('Count is', countSignal.get());
|
|
93
|
+
});
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
#### Parameters
|
|
97
|
+
|
|
98
|
+
- `fn` (`() => T`): A function that will run whenever its tracked signals update.
|
|
99
|
+
|
|
100
|
+
#### Returns
|
|
101
|
+
|
|
102
|
+
- `Effect<T>`: The created effect object.
|
|
103
|
+
|
|
104
|
+
### `createSignalScope`
|
|
105
|
+
|
|
106
|
+
Creates an Alien Signals effect scope. This scope can manage multiple effects, allowing you to stop or start them together.
|
|
107
|
+
|
|
108
|
+
#### Example
|
|
109
|
+
|
|
110
|
+
```typescript
|
|
111
|
+
const scope = createSignalScope();
|
|
112
|
+
scope.run(() => {
|
|
113
|
+
// create effects in here...
|
|
114
|
+
});
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
#### Returns
|
|
118
|
+
|
|
119
|
+
- `EffectScope`: The created effect scope.
|
|
120
|
+
|
|
121
|
+
### `unstable_createAsyncComputed`
|
|
122
|
+
|
|
123
|
+
Creates an async computed signal in Alien Signals. The getter is an async generator that yields dependencies and finally resolves to a computed value.
|
|
124
|
+
|
|
125
|
+
#### Example
|
|
126
|
+
|
|
127
|
+
```typescript
|
|
128
|
+
const asyncComp = createAsyncComputed<number>(async function* () {
|
|
129
|
+
yield someDependency;
|
|
130
|
+
return 42;
|
|
131
|
+
});
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
#### Parameters
|
|
135
|
+
|
|
136
|
+
- `getter` (`() => AsyncGenerator<Dependency, T>`): An async generator returning dependencies and ultimately a value.
|
|
137
|
+
|
|
138
|
+
#### Returns
|
|
139
|
+
|
|
140
|
+
- `AsyncComputed<T>`: The created async computed signal.
|
|
141
|
+
|
|
142
|
+
### `unstable_createAsyncEffect`
|
|
143
|
+
|
|
144
|
+
Creates an async effect in Alien Signals. The function is an async generator that yields dependencies as they are discovered.
|
|
145
|
+
|
|
146
|
+
#### Example
|
|
147
|
+
|
|
148
|
+
```typescript
|
|
149
|
+
createAsyncEffect(async function* () {
|
|
150
|
+
yield someDependency;
|
|
151
|
+
console.log('Async effect done!');
|
|
152
|
+
});
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
#### Parameters
|
|
156
|
+
|
|
157
|
+
- `fn` (`() => AsyncGenerator<Dependency, T>`): An async generator returning dependencies.
|
|
158
|
+
|
|
159
|
+
#### Returns
|
|
160
|
+
|
|
161
|
+
- `Promise<T>`: The created async effect object.
|
|
162
|
+
|
|
163
|
+
### `unstable_createComputedArray`
|
|
164
|
+
|
|
165
|
+
Creates a computed array signal in Alien Signals, deriving a reactive array from an original signal array.
|
|
166
|
+
|
|
167
|
+
#### Example
|
|
168
|
+
|
|
169
|
+
```typescript
|
|
170
|
+
const numbersSignal = createSignal([1, 2, 3]);
|
|
171
|
+
const compArray = createComputedArray(numbersSignal, (itemSignal, i) => () => {
|
|
172
|
+
return itemSignal.get() * 2;
|
|
173
|
+
});
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
#### Parameters
|
|
177
|
+
|
|
178
|
+
- `arr` (`ISignal<I[]>`): Signal containing an array.
|
|
179
|
+
- `getGetter` (`(itemSignal: ISignal<I>, index: number) => () => O`): A function returning a getter for each item signal.
|
|
180
|
+
|
|
181
|
+
#### Returns
|
|
182
|
+
|
|
183
|
+
- `Readonly<O[]>`: A proxied array signal.
|
|
184
|
+
|
|
185
|
+
### `unstable_createComputedSet`
|
|
186
|
+
|
|
187
|
+
Creates a computed Set signal in Alien Signals that tracks changes to a source Set signal.
|
|
188
|
+
|
|
189
|
+
#### Example
|
|
190
|
+
|
|
191
|
+
```typescript
|
|
192
|
+
const setSignal = createSignal(new Set([1, 2]));
|
|
193
|
+
const compSet = createComputedSet(setSignal);
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
#### Parameters
|
|
197
|
+
|
|
198
|
+
- `source` (`IWritableSignal<Set<T>>`): A signal containing a Set.
|
|
199
|
+
|
|
200
|
+
#### Returns
|
|
201
|
+
|
|
202
|
+
- `ISignal<Set<T>>`: A computed signal referencing that Set.
|
|
203
|
+
|
|
204
|
+
### `unstable_createEqualityComputed`
|
|
205
|
+
|
|
206
|
+
Creates an equality-based computed signal, only updating when the new value is not deeply equal to the old value.
|
|
207
|
+
|
|
208
|
+
#### Example
|
|
209
|
+
|
|
210
|
+
```typescript
|
|
211
|
+
const eqComp = createEqualityComputed(() => {
|
|
212
|
+
return { foo: 'bar' };
|
|
213
|
+
});
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
#### Parameters
|
|
217
|
+
|
|
218
|
+
- `getter` (`() => T`): A function returning the value to compare.
|
|
219
|
+
|
|
220
|
+
#### Returns
|
|
221
|
+
|
|
222
|
+
- `ISignal<T>`: An equality computed signal.
|
|
223
|
+
|
|
224
|
+
### `useSignal`
|
|
225
|
+
|
|
226
|
+
React hook returning `[value, setValue]` for a given Alien Signal. Uses `useSyncExternalStore` for concurrency-safe re-renders.
|
|
227
|
+
|
|
228
|
+
#### Example
|
|
229
|
+
|
|
230
|
+
```typescript
|
|
231
|
+
const countSignal = createSignal(0);
|
|
232
|
+
function Counter() {
|
|
233
|
+
const [count, setCount] = useSignal(countSignal);
|
|
234
|
+
return <button onClick={() => setCount(count + 1)}>{count}</button>;
|
|
235
|
+
}
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
#### Parameters
|
|
239
|
+
|
|
240
|
+
- `alienSignal` (`IWritableSignal<T>`): The signal to read/write.
|
|
241
|
+
|
|
242
|
+
#### Returns
|
|
243
|
+
|
|
244
|
+
- `[T, (val: T | ((oldVal: T) => T)) => void]`: A tuple `[currentValue, setValue]`.
|
|
245
|
+
|
|
246
|
+
### `useSignalValue`
|
|
247
|
+
|
|
248
|
+
React hook returning only the current value of an Alien Signal (or computed). No setter is provided.
|
|
249
|
+
|
|
250
|
+
#### Example
|
|
251
|
+
|
|
252
|
+
```typescript
|
|
253
|
+
const countSignal = createSignal(0);
|
|
254
|
+
const doubleSignal = createComputed(() => countSignal.get() * 2);
|
|
255
|
+
function Display() {
|
|
256
|
+
const count = useSignalValue(countSignal);
|
|
257
|
+
const double = useSignalValue(doubleSignal);
|
|
258
|
+
return <div>{count}, {double}</div>;
|
|
259
|
+
}
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
#### Parameters
|
|
263
|
+
|
|
264
|
+
- `alienSignal` (`IWritableSignal<T>`): The signal to read.
|
|
265
|
+
|
|
266
|
+
#### Returns
|
|
267
|
+
|
|
268
|
+
- `T`: The current value.
|
|
269
|
+
|
|
270
|
+
### `useSetSignal`
|
|
271
|
+
|
|
272
|
+
React hook returning only a setter function for an Alien Signal. No current value is provided, similar to Jotai's `useSetAtom`.
|
|
273
|
+
|
|
274
|
+
#### Example
|
|
275
|
+
|
|
276
|
+
```typescript
|
|
277
|
+
const countSignal = createSignal(0);
|
|
278
|
+
function Incrementor() {
|
|
279
|
+
const setCount = useSetSignal(countSignal);
|
|
280
|
+
return <button onClick={() => setCount((c) => c + 1)}>+1</button>;
|
|
281
|
+
}
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
#### Parameters
|
|
285
|
+
|
|
286
|
+
- `alienSignal` (`IWritableSignal<T>`): The signal to write.
|
|
287
|
+
|
|
288
|
+
#### Returns
|
|
289
|
+
|
|
290
|
+
- `(val: T | ((oldVal: T) => T)) => void`: A setter function.
|
|
291
|
+
|
|
292
|
+
### `useSignalEffect`
|
|
293
|
+
|
|
294
|
+
React hook for running a side effect whenever Alien Signals' dependencies used in `fn` change. The effect is cleaned up on component unmount.
|
|
295
|
+
|
|
296
|
+
#### Example
|
|
297
|
+
|
|
298
|
+
```typescript
|
|
299
|
+
function Logger() {
|
|
300
|
+
useSignalEffect(() => {
|
|
301
|
+
console.log('Signal changed:', someSignal.get());
|
|
302
|
+
});
|
|
303
|
+
return null;
|
|
304
|
+
}
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
#### Parameters
|
|
308
|
+
|
|
309
|
+
- `fn` (`() => void`): The effect function to run.
|
|
310
|
+
|
|
311
|
+
### `useSignalScope`
|
|
312
|
+
|
|
313
|
+
React hook for managing an Alien Signals effect scope. All signals/effects created inside this scope run when the component mounts, and are stopped automatically when the component unmounts.
|
|
314
|
+
|
|
315
|
+
#### Example
|
|
316
|
+
|
|
317
|
+
```typescript
|
|
318
|
+
function ScopedEffects() {
|
|
319
|
+
const scope = useSignalScope();
|
|
320
|
+
useEffect(() => {
|
|
321
|
+
scope.run(() => {
|
|
322
|
+
createEffect(() => {
|
|
323
|
+
console.log('Scoped effect:', someSignal.get());
|
|
324
|
+
});
|
|
325
|
+
});
|
|
326
|
+
}, [scope]);
|
|
327
|
+
return null;
|
|
328
|
+
}
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
#### Returns
|
|
332
|
+
|
|
333
|
+
- `EffectScope`: The created effect scope.
|
|
334
|
+
|
|
335
|
+
### `unstable_useAsyncComputedValue`
|
|
336
|
+
|
|
337
|
+
React hook to read from an async computed signal. The hook fetches the current value, subscribing to changes via `useSyncExternalStore`, and triggers a `get()` call to retrieve updated data. Maintains an internal state for the resolved value of the promise.
|
|
338
|
+
|
|
339
|
+
#### Example
|
|
340
|
+
|
|
341
|
+
```typescript
|
|
342
|
+
const asyncSignal = createAsyncComputed<number>(async function*() {
|
|
343
|
+
const val = someSignal.get();
|
|
344
|
+
yield Promise.resolve(someSignal); // track async dep
|
|
345
|
+
return val * 2;
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
function AsyncDisplay() {
|
|
349
|
+
const value = useAsyncComputedValue(asyncSignal);
|
|
350
|
+
return <div>Value: {String(value)}</div>;
|
|
351
|
+
}
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
#### Parameters
|
|
355
|
+
|
|
356
|
+
- `alienAsyncComp` (`AsyncComputed<T>`): The async computed signal to read.
|
|
357
|
+
|
|
358
|
+
#### Returns
|
|
359
|
+
|
|
360
|
+
- `T | undefined`: The resolved value (or undefined if not yet resolved).
|
|
361
|
+
|
|
362
|
+
### `unstable_useAsyncEffect`
|
|
363
|
+
|
|
364
|
+
React hook to run an asynchronous effect whenever the component mounts, cleaning up when it unmounts.
|
|
365
|
+
|
|
366
|
+
#### Example
|
|
367
|
+
|
|
368
|
+
```typescript
|
|
369
|
+
useAsyncEffect(async function* () {
|
|
370
|
+
yield someDependency;
|
|
371
|
+
console.log('Async side effect complete!');
|
|
372
|
+
});
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
#### Parameters
|
|
376
|
+
|
|
377
|
+
- `fn` (`() => AsyncGenerator<Dependency, T>`): An async generator representing the effect logic.
|
|
378
|
+
|
|
379
|
+
|
|
380
|
+
## Refer
|
|
381
|
+
|
|
382
|
+
React Alien Signals is a **TypeScript** library that provides hooks built on top of [Alien Signals](https://github.com/stackblitz/alien-signals).
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
import { IWritableSignal, ISignal, Effect, EffectScope, Dependency, Computed } from 'alien-signals';
|
|
2
|
+
|
|
2
3
|
/**
|
|
3
4
|
*
|
|
4
5
|
* React Alien Signals is a **TypeScript** library that provides hooks built on top of [Alien Signals](https://github.com/stackblitz/alien-signals).
|
|
@@ -7,27 +8,10 @@
|
|
|
7
8
|
* @module reactjs-signal
|
|
8
9
|
*/
|
|
9
10
|
|
|
10
|
-
import {
|
|
11
|
-
computed as alienComputed,
|
|
12
|
-
effect as alienEffect,
|
|
13
|
-
effectScope as alienEffectScope,
|
|
14
|
-
signal as alienSignal,
|
|
15
|
-
unstable as alienUnstable,
|
|
16
|
-
type Effect,
|
|
17
|
-
type Computed,
|
|
18
|
-
type Dependency,
|
|
19
|
-
type EffectScope,
|
|
20
|
-
type ISignal,
|
|
21
|
-
type IWritableSignal,
|
|
22
|
-
} from 'alien-signals';
|
|
23
|
-
import { useEffect, useMemo, useState, useSyncExternalStore } from 'react';
|
|
24
|
-
|
|
25
11
|
declare class AsyncComputed<T = any> extends Computed {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
update(): Promise<boolean>;
|
|
12
|
+
get(): Promise<T>;
|
|
13
|
+
update(): Promise<boolean>;
|
|
29
14
|
}
|
|
30
|
-
|
|
31
15
|
/**
|
|
32
16
|
* Creates a writable Alien Signal.
|
|
33
17
|
*
|
|
@@ -41,10 +25,7 @@ declare class AsyncComputed<T = any> extends Computed {
|
|
|
41
25
|
* @param {T} initialValue - The initial value of the signal.
|
|
42
26
|
* @returns {IWritableSignal<T>} The created Alien Signal.
|
|
43
27
|
*/
|
|
44
|
-
|
|
45
|
-
return alienSignal<T>(initialValue);
|
|
46
|
-
}
|
|
47
|
-
|
|
28
|
+
declare function createSignal<T>(initialValue: T): IWritableSignal<T>;
|
|
48
29
|
/**
|
|
49
30
|
* Creates a computed Alien Signal based on a getter function.
|
|
50
31
|
*
|
|
@@ -58,10 +39,7 @@ export function createSignal<T>(initialValue: T): IWritableSignal<T> {
|
|
|
58
39
|
* @param {() => T} fn - A getter function returning a computed value.
|
|
59
40
|
* @returns {ISignal<T>} The created computed signal.
|
|
60
41
|
*/
|
|
61
|
-
|
|
62
|
-
return alienComputed<T>(fn);
|
|
63
|
-
}
|
|
64
|
-
|
|
42
|
+
declare function createComputed<T>(fn: () => T): ISignal<T>;
|
|
65
43
|
/**
|
|
66
44
|
* Creates a side effect in Alien Signals.
|
|
67
45
|
*
|
|
@@ -77,10 +55,7 @@ export function createComputed<T>(fn: () => T): ISignal<T> {
|
|
|
77
55
|
* @param {() => T} fn - A function that will run whenever its tracked signals update.
|
|
78
56
|
* @returns {Effect<T>} The created effect object.
|
|
79
57
|
*/
|
|
80
|
-
|
|
81
|
-
return alienEffect(fn);
|
|
82
|
-
}
|
|
83
|
-
|
|
58
|
+
declare function createEffect<T>(fn: () => T): Effect<T>;
|
|
84
59
|
/**
|
|
85
60
|
* Creates an Alien Signals effect scope. This scope can manage multiple effects,
|
|
86
61
|
* allowing you to stop or start them together.
|
|
@@ -95,10 +70,7 @@ export function createEffect<T>(fn: () => T): Effect<T> {
|
|
|
95
70
|
*
|
|
96
71
|
* @returns {EffectScope} The created effect scope.
|
|
97
72
|
*/
|
|
98
|
-
|
|
99
|
-
return alienEffectScope();
|
|
100
|
-
}
|
|
101
|
-
|
|
73
|
+
declare function createSignalScope(): EffectScope;
|
|
102
74
|
/**
|
|
103
75
|
* Creates an async computed signal in Alien Signals. The getter is an async generator
|
|
104
76
|
* that yields dependencies and finally resolves to a computed value.
|
|
@@ -116,12 +88,7 @@ export function createSignalScope(): EffectScope {
|
|
|
116
88
|
* @returns {AsyncComputed<T>} The created async computed signal.
|
|
117
89
|
* @experimental
|
|
118
90
|
*/
|
|
119
|
-
|
|
120
|
-
getter: () => AsyncGenerator<Dependency, T>,
|
|
121
|
-
): AsyncComputed<T> {
|
|
122
|
-
return alienUnstable.asyncComputed<T>(getter);
|
|
123
|
-
}
|
|
124
|
-
|
|
91
|
+
declare function unstable_createAsyncComputed<T>(getter: () => AsyncGenerator<Dependency, T>): AsyncComputed<T>;
|
|
125
92
|
/**
|
|
126
93
|
* Creates an async effect in Alien Signals. The function is an async generator
|
|
127
94
|
* that yields dependencies as they are discovered.
|
|
@@ -138,16 +105,7 @@ export function unstable_createAsyncComputed<T>(
|
|
|
138
105
|
* @param {() => AsyncGenerator<Dependency, T>} fn - An async generator returning dependencies.
|
|
139
106
|
* @returns {Promise<T>} The created async effect object.
|
|
140
107
|
*/
|
|
141
|
-
|
|
142
|
-
fn: () => AsyncGenerator<Dependency, T>,
|
|
143
|
-
): Promise<T> {
|
|
144
|
-
const eff = alienUnstable.asyncEffect(fn);
|
|
145
|
-
|
|
146
|
-
// Immediately run the effect and return its promise
|
|
147
|
-
const final = await eff.run();
|
|
148
|
-
return final;
|
|
149
|
-
}
|
|
150
|
-
|
|
108
|
+
declare function unstable_createAsyncEffect<T>(fn: () => AsyncGenerator<Dependency, T>): Promise<T>;
|
|
151
109
|
/**
|
|
152
110
|
* Creates a computed array signal in Alien Signals, deriving a reactive
|
|
153
111
|
* array from an original signal array.
|
|
@@ -167,13 +125,7 @@ export async function unstable_createAsyncEffect<T>(
|
|
|
167
125
|
* @returns {Readonly<O[]>} A proxied array signal.
|
|
168
126
|
* @experimental
|
|
169
127
|
*/
|
|
170
|
-
|
|
171
|
-
arr: ISignal<I[]>,
|
|
172
|
-
getGetter: (itemSignal: ISignal<I>, index: number) => () => O,
|
|
173
|
-
): Readonly<O[]> {
|
|
174
|
-
return alienUnstable.computedArray<I, O>(arr, getGetter);
|
|
175
|
-
}
|
|
176
|
-
|
|
128
|
+
declare function unstable_createComputedArray<I, O>(arr: ISignal<I[]>, getGetter: (itemSignal: ISignal<I>, index: number) => () => O): Readonly<O[]>;
|
|
177
129
|
/**
|
|
178
130
|
* Creates a computed Set signal in Alien Signals that tracks changes
|
|
179
131
|
* to a source Set signal.
|
|
@@ -189,10 +141,7 @@ export function unstable_createComputedArray<I, O>(
|
|
|
189
141
|
* @returns {ISignal<Set<T>>} A computed signal referencing that Set.
|
|
190
142
|
* @experimental
|
|
191
143
|
*/
|
|
192
|
-
|
|
193
|
-
return alienUnstable.computedSet<T>(source);
|
|
194
|
-
}
|
|
195
|
-
|
|
144
|
+
declare function unstable_createComputedSet<T>(source: IWritableSignal<Set<T>>): ISignal<Set<T>>;
|
|
196
145
|
/**
|
|
197
146
|
* Creates an equality-based computed signal, only updating when the new value
|
|
198
147
|
* is not deeply equal to the old value.
|
|
@@ -209,10 +158,7 @@ export function unstable_createComputedSet<T>(source: IWritableSignal<Set<T>>):
|
|
|
209
158
|
* @returns {ISignal<T>} An equality computed signal.
|
|
210
159
|
* @experimental
|
|
211
160
|
*/
|
|
212
|
-
|
|
213
|
-
return alienUnstable.equalityComputed(getter);
|
|
214
|
-
}
|
|
215
|
-
|
|
161
|
+
declare function unstable_createEqualityComputed<T>(getter: () => T): ISignal<T>;
|
|
216
162
|
/**
|
|
217
163
|
* React hook returning `[value, setValue]` for a given Alien Signal.
|
|
218
164
|
* Uses useSyncExternalStore for concurrency-safe re-renders.
|
|
@@ -230,32 +176,7 @@ export function unstable_createEqualityComputed<T>(getter: () => T): ISignal<T>
|
|
|
230
176
|
* @param {IWritableSignal<T>} alienSignal - The signal to read/write.
|
|
231
177
|
* @returns {[T, (val: T | ((oldVal: T) => T)) => void]} A tuple [currentValue, setValue].
|
|
232
178
|
*/
|
|
233
|
-
|
|
234
|
-
alienSignal: IWritableSignal<T>,
|
|
235
|
-
): [T, (val: T | ((oldVal: T) => T)) => void] {
|
|
236
|
-
const value = useSyncExternalStore(
|
|
237
|
-
(callback) => {
|
|
238
|
-
const eff = alienEffect(() => {
|
|
239
|
-
alienSignal.get(); // track
|
|
240
|
-
callback();
|
|
241
|
-
});
|
|
242
|
-
return () => eff.stop();
|
|
243
|
-
},
|
|
244
|
-
() => alienSignal.get(),
|
|
245
|
-
() => alienSignal.get(), // server snapshot
|
|
246
|
-
);
|
|
247
|
-
|
|
248
|
-
const setValue = (val: T | ((oldVal: T) => T)) => {
|
|
249
|
-
if (typeof val === 'function') {
|
|
250
|
-
alienSignal.set((val as (oldVal: T) => T)(alienSignal.get()));
|
|
251
|
-
} else {
|
|
252
|
-
alienSignal.set(val);
|
|
253
|
-
}
|
|
254
|
-
};
|
|
255
|
-
|
|
256
|
-
return [value, setValue];
|
|
257
|
-
}
|
|
258
|
-
|
|
179
|
+
declare function useSignal<T>(alienSignal: IWritableSignal<T>): [T, (val: T | ((oldVal: T) => T)) => void];
|
|
259
180
|
/**
|
|
260
181
|
* React hook returning only the current value of an Alien Signal (or computed).
|
|
261
182
|
* No setter is provided.
|
|
@@ -275,19 +196,7 @@ export function useSignal<T>(
|
|
|
275
196
|
* @param {IWritableSignal<T>} alienSignal - The signal to read.
|
|
276
197
|
* @returns {T} The current value.
|
|
277
198
|
*/
|
|
278
|
-
|
|
279
|
-
return useSyncExternalStore(
|
|
280
|
-
(callback) => {
|
|
281
|
-
const eff = alienEffect(() => {
|
|
282
|
-
alienSignal.get();
|
|
283
|
-
callback();
|
|
284
|
-
});
|
|
285
|
-
return () => eff.stop();
|
|
286
|
-
},
|
|
287
|
-
() => alienSignal.get(),
|
|
288
|
-
);
|
|
289
|
-
}
|
|
290
|
-
|
|
199
|
+
declare function useSignalValue<T>(alienSignal: IWritableSignal<T>): T;
|
|
291
200
|
/**
|
|
292
201
|
* React hook returning only a setter function for an Alien Signal.
|
|
293
202
|
* No current value is provided, similar to Jotai's useSetAtom.
|
|
@@ -305,18 +214,7 @@ export function useSignalValue<T>(alienSignal: IWritableSignal<T>): T {
|
|
|
305
214
|
* @param {IWritableSignal<T>} alienSignal - The signal to write.
|
|
306
215
|
* @returns {(val: T | ((oldVal: T) => T)) => void} A setter function.
|
|
307
216
|
*/
|
|
308
|
-
|
|
309
|
-
alienSignal: IWritableSignal<T>,
|
|
310
|
-
): (val: T | ((oldVal: T) => T)) => void {
|
|
311
|
-
return (val) => {
|
|
312
|
-
if (typeof val === 'function') {
|
|
313
|
-
alienSignal.set((val as (oldVal: T) => T)(alienSignal.get()));
|
|
314
|
-
} else {
|
|
315
|
-
alienSignal.set(val);
|
|
316
|
-
}
|
|
317
|
-
};
|
|
318
|
-
}
|
|
319
|
-
|
|
217
|
+
declare function useSetSignal<T>(alienSignal: IWritableSignal<T>): (val: T | ((oldVal: T) => T)) => void;
|
|
320
218
|
/**
|
|
321
219
|
* React hook for running a side effect whenever Alien Signals' dependencies
|
|
322
220
|
* used in `fn` change. The effect is cleaned up on component unmount.
|
|
@@ -333,13 +231,7 @@ export function useSetSignal<T>(
|
|
|
333
231
|
*
|
|
334
232
|
* @param {() => void} fn - The effect function to run.
|
|
335
233
|
*/
|
|
336
|
-
|
|
337
|
-
useEffect(() => {
|
|
338
|
-
const eff = alienEffect(fn);
|
|
339
|
-
return () => eff.stop();
|
|
340
|
-
}, [fn]);
|
|
341
|
-
}
|
|
342
|
-
|
|
234
|
+
declare function useSignalEffect(fn: () => void): void;
|
|
343
235
|
/**
|
|
344
236
|
* React hook for managing an Alien Signals effect scope.
|
|
345
237
|
* All signals/effects created inside this scope run when the component mounts,
|
|
@@ -362,16 +254,7 @@ export function useSignalEffect(fn: () => void): void {
|
|
|
362
254
|
*
|
|
363
255
|
* @returns {EffectScope} The created effect scope.
|
|
364
256
|
*/
|
|
365
|
-
|
|
366
|
-
const scope = useMemo(() => alienEffectScope(), []);
|
|
367
|
-
useEffect(() => {
|
|
368
|
-
return () => {
|
|
369
|
-
scope.stop();
|
|
370
|
-
};
|
|
371
|
-
}, [scope]);
|
|
372
|
-
return scope;
|
|
373
|
-
}
|
|
374
|
-
|
|
257
|
+
declare function useSignalScope(): EffectScope;
|
|
375
258
|
/**
|
|
376
259
|
* React hook to read from an async computed signal.
|
|
377
260
|
* The hook fetches the current value, subscribing to changes via useSyncExternalStore,
|
|
@@ -397,35 +280,7 @@ export function useSignalScope(): EffectScope {
|
|
|
397
280
|
* @returns {T | undefined} The resolved value (or undefined if not yet resolved).
|
|
398
281
|
* @experimental
|
|
399
282
|
*/
|
|
400
|
-
|
|
401
|
-
const [value, setValue] = useState<T | undefined>(alienAsyncComp.currentValue);
|
|
402
|
-
|
|
403
|
-
useSyncExternalStore(
|
|
404
|
-
(callback) => {
|
|
405
|
-
const eff = alienEffect(() => {
|
|
406
|
-
alienAsyncComp.currentValue; // track
|
|
407
|
-
callback();
|
|
408
|
-
});
|
|
409
|
-
return () => eff.stop();
|
|
410
|
-
},
|
|
411
|
-
() => alienAsyncComp.currentValue,
|
|
412
|
-
);
|
|
413
|
-
|
|
414
|
-
useEffect(() => {
|
|
415
|
-
let active = true;
|
|
416
|
-
const fetchValue = async () => {
|
|
417
|
-
const val = await alienAsyncComp.get();
|
|
418
|
-
if (active) setValue(val);
|
|
419
|
-
};
|
|
420
|
-
fetchValue();
|
|
421
|
-
return () => {
|
|
422
|
-
active = false;
|
|
423
|
-
};
|
|
424
|
-
}, [alienAsyncComp]);
|
|
425
|
-
|
|
426
|
-
return value;
|
|
427
|
-
}
|
|
428
|
-
|
|
283
|
+
declare function unstable_useAsyncComputedValue<T>(alienAsyncComp: AsyncComputed<T>): T | undefined;
|
|
429
284
|
/**
|
|
430
285
|
* React hook to run an asynchronous effect whenever the component mounts,
|
|
431
286
|
* cleaning up when it unmounts.
|
|
@@ -442,9 +297,6 @@ export function unstable_useAsyncComputedValue<T>(alienAsyncComp: AsyncComputed<
|
|
|
442
297
|
* @param {() => AsyncGenerator<Dependency, T>} fn - An async generator representing the effect logic.
|
|
443
298
|
* @experimental
|
|
444
299
|
*/
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
return () => eff.stop();
|
|
449
|
-
}, [fn]);
|
|
450
|
-
}
|
|
300
|
+
declare function unstable_useAsyncEffect<T>(fn: () => AsyncGenerator<Dependency, T>): void;
|
|
301
|
+
|
|
302
|
+
export { createComputed, createEffect, createSignal, createSignalScope, unstable_createAsyncComputed, unstable_createAsyncEffect, unstable_createComputedArray, unstable_createComputedSet, unstable_createEqualityComputed, unstable_useAsyncComputedValue, unstable_useAsyncEffect, useSetSignal, useSignal, useSignalEffect, useSignalScope, useSignalValue };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import {signal,computed,effect,effectScope,unstable}from'alien-signals';import {useSyncExternalStore,useEffect,useMemo,useState}from'react';function g(e){return signal(e)}function m(e){return computed(e)}function x(e){return effect(e)}function b(){return effectScope()}function E(e){return unstable.asyncComputed(e)}async function I(e){return await unstable.asyncEffect(e).run()}function V(e,t){return unstable.computedArray(e,t)}function C(e){return unstable.computedSet(e)}function v(e){return unstable.equalityComputed(e)}function A(e){return [useSyncExternalStore(n=>{let c=effect(()=>{e.get(),n();});return ()=>c.stop()},()=>e.get(),()=>e.get()),n=>{typeof n=="function"?e.set(n(e.get())):e.set(n);}]}function _(e){return useSyncExternalStore(t=>{let o=effect(()=>{e.get(),t();});return ()=>o.stop()},()=>e.get())}function W(e){return t=>{typeof t=="function"?e.set(t(e.get())):e.set(t);}}function D(e){useEffect(()=>{let t=effect(e);return ()=>t.stop()},[e]);}function O(){let e=useMemo(()=>effectScope(),[]);return useEffect(()=>()=>{e.stop();},[e]),e}function G(e){let[t,o]=useState(e.currentValue);return useSyncExternalStore(n=>{let c=effect(()=>{e.currentValue,n();});return ()=>c.stop()},()=>e.currentValue),useEffect(()=>{let n=!0;return (async()=>{let l=await e.get();n&&o(l);})(),()=>{n=!1;}},[e]),t}function P(e){useEffect(()=>{let t=unstable.asyncEffect(e);return ()=>t.stop()},[e]);}export{m as createComputed,x as createEffect,g as createSignal,b as createSignalScope,E as unstable_createAsyncComputed,I as unstable_createAsyncEffect,V as unstable_createComputedArray,C as unstable_createComputedSet,v as unstable_createEqualityComputed,G as unstable_useAsyncComputedValue,P as unstable_useAsyncEffect,W as useSetSignal,A as useSignal,D as useSignalEffect,O as useSignalScope,_ as useSignalValue};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "reactjs-signal",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -8,9 +8,13 @@
|
|
|
8
8
|
"exports": {
|
|
9
9
|
".": {
|
|
10
10
|
"types": "./dist/index.d.ts",
|
|
11
|
-
"import": "./dist/index.js"
|
|
11
|
+
"import": "./dist/index.js",
|
|
12
|
+
"require": "./dist/index.js"
|
|
12
13
|
}
|
|
13
14
|
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist"
|
|
17
|
+
],
|
|
14
18
|
"scripts": {
|
|
15
19
|
"build": "tsup",
|
|
16
20
|
"watch": "npm run build -- --watch src",
|
|
@@ -40,5 +44,13 @@
|
|
|
40
44
|
"peerDependencies": {
|
|
41
45
|
"alien-signals": ">=0.4",
|
|
42
46
|
"react": ">=18"
|
|
47
|
+
},
|
|
48
|
+
"bugs": {
|
|
49
|
+
"url": "https://github.com/hunghg255/reactjs-signal/issues"
|
|
50
|
+
},
|
|
51
|
+
"homepage": "https://github.com/hunghg255/reactjs-signal#readme",
|
|
52
|
+
"repository": {
|
|
53
|
+
"type": "git",
|
|
54
|
+
"url": "git+https://github.com/hunghg255/reactjs-signal.git"
|
|
43
55
|
}
|
|
44
56
|
}
|
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
## Git Commit Message Convention
|
|
2
|
-
|
|
3
|
-
> This is adapted from [Commit convention](https://www.conventionalcommits.org/en/v1.0.0/).
|
|
4
|
-
|
|
5
|
-
#### TL;DR:
|
|
6
|
-
|
|
7
|
-
Messages must be matched by the following regex:
|
|
8
|
-
|
|
9
|
-
```js
|
|
10
|
-
/^((feat|fix|docs|style|core|i18n|report|misc|cli|audits|refactor|perf|test|workflow|build|ci|chore|types|wip|release|deps?|merge|examples?|revert)(\(.+\))?(\:|\!\:)|(Merge|Revert|Version)) .{1,50}$/;
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
#### Examples
|
|
14
|
-
|
|
15
|
-
Appears under "Features" header, `compiler` subheader:
|
|
16
|
-
|
|
17
|
-
```
|
|
18
|
-
feat(compiler): add 'comments' option
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
Appears under "Bug Fixes" header, `v-model` subheader, with a link to issue #28:
|
|
22
|
-
|
|
23
|
-
```
|
|
24
|
-
fix(v-model): handle events on blur
|
|
25
|
-
|
|
26
|
-
close #28
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
Appears under "Performance Improvements" header, and under "Breaking Changes" with the breaking change explanation:
|
|
30
|
-
|
|
31
|
-
```
|
|
32
|
-
perf(core): improve vdom diffing by removing 'foo' option
|
|
33
|
-
|
|
34
|
-
BREAKING CHANGE: The 'foo' option has been removed.
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
The following commit and commit `667ecc1` do not appear in the changelog if they are under the same release. If not, the revert commit appears under the "Reverts" header.
|
|
38
|
-
|
|
39
|
-
```
|
|
40
|
-
revert: feat(compiler): add 'comments' option
|
|
41
|
-
|
|
42
|
-
This reverts commit 667ecc1654a317a13331b17617d973392f415f02.
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
### Full Message Format
|
|
46
|
-
|
|
47
|
-
A commit message consists of a **header**, **body** and **footer**. The header has a **type**, **scope** and **subject**:
|
|
48
|
-
|
|
49
|
-
```
|
|
50
|
-
<type>(<scope>): <subject>
|
|
51
|
-
<BLANK LINE>
|
|
52
|
-
<body>
|
|
53
|
-
<BLANK LINE>
|
|
54
|
-
<footer>
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
The **header** is mandatory and the **scope** of the header is optional.
|
|
58
|
-
|
|
59
|
-
### Revert
|
|
60
|
-
|
|
61
|
-
If the commit reverts a previous commit, it should begin with `revert: `, followed by the header of the reverted commit. In the body, it should say: `This reverts commit <hash>.`, where the hash is the SHA of the commit being reverted.
|
|
62
|
-
|
|
63
|
-
### Type
|
|
64
|
-
|
|
65
|
-
If the prefix is `feat`, `fix` or `perf`, it will appear in the changelog. However, if there is any [BREAKING CHANGE](#footer), the commit will always appear in the changelog.
|
|
66
|
-
|
|
67
|
-
Other prefixes are up to your discretion. Suggested prefixes are `docs`, `chore`, `style`, `refactor`, and `test` for non-changelog related tasks.
|
|
68
|
-
|
|
69
|
-
### Scope
|
|
70
|
-
|
|
71
|
-
The scope could be anything specifying the place of the commit change. For example `core`, `compiler`, `ssr`, `v-model`, `transition` etc...
|
|
72
|
-
|
|
73
|
-
### Subject
|
|
74
|
-
|
|
75
|
-
The subject contains a succinct description of the change:
|
|
76
|
-
|
|
77
|
-
- use the imperative, present tense: "change" not "changed" nor "changes"
|
|
78
|
-
- don't capitalize the first letter
|
|
79
|
-
- no dot (.) at the end
|
|
80
|
-
|
|
81
|
-
### Body
|
|
82
|
-
|
|
83
|
-
Just as in the **subject**, use the imperative, present tense: "change" not "changed" nor "changes".
|
|
84
|
-
The body should include the motivation for the change and contrast this with previous behavior.
|
|
85
|
-
|
|
86
|
-
### Footer
|
|
87
|
-
|
|
88
|
-
The footer should contain any information about **Breaking Changes** and is also the place to
|
|
89
|
-
reference GitHub issues that this commit **Closes**.
|
|
90
|
-
|
|
91
|
-
**Breaking Changes** should start with the word `BREAKING CHANGE:` with a space or two newlines. The rest of the commit message is then used for this.
|
|
92
|
-
|
|
93
|
-
```
|
|
94
|
-
feat!: breaking change / feat(scope)!: rework API
|
|
95
|
-
```
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
name: CI
|
|
2
|
-
on:
|
|
3
|
-
push:
|
|
4
|
-
branches:
|
|
5
|
-
- master
|
|
6
|
-
|
|
7
|
-
pull_request:
|
|
8
|
-
branches:
|
|
9
|
-
- master
|
|
10
|
-
|
|
11
|
-
jobs:
|
|
12
|
-
build:
|
|
13
|
-
runs-on: ubuntu-latest
|
|
14
|
-
steps:
|
|
15
|
-
- uses: actions/checkout@v3
|
|
16
|
-
- uses: actions/setup-node@v3
|
|
17
|
-
with:
|
|
18
|
-
node-version: 18.x
|
|
19
|
-
|
|
20
|
-
- run: npm install
|
|
21
|
-
- run: npm run lint && npm run build
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
name: Release
|
|
2
|
-
|
|
3
|
-
permissions:
|
|
4
|
-
contents: write
|
|
5
|
-
|
|
6
|
-
on:
|
|
7
|
-
push:
|
|
8
|
-
tags:
|
|
9
|
-
- 'v*'
|
|
10
|
-
|
|
11
|
-
jobs:
|
|
12
|
-
release:
|
|
13
|
-
runs-on: ubuntu-latest
|
|
14
|
-
steps:
|
|
15
|
-
- uses: actions/checkout@v3
|
|
16
|
-
with:
|
|
17
|
-
fetch-depth: 0
|
|
18
|
-
|
|
19
|
-
- uses: actions/setup-node@v3
|
|
20
|
-
with:
|
|
21
|
-
node-version: 18.x
|
|
22
|
-
|
|
23
|
-
- run: npx changeloggithub@latest
|
|
24
|
-
env:
|
|
25
|
-
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
|
|
26
|
-
|
|
27
|
-
- name: Publish
|
|
28
|
-
run: |
|
|
29
|
-
echo "Publishing"
|
package/tsconfig.json
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
// Enable latest features
|
|
4
|
-
"lib": ["ESNext", "DOM"],
|
|
5
|
-
"target": "ESNext",
|
|
6
|
-
"module": "ESNext",
|
|
7
|
-
"moduleDetection": "force",
|
|
8
|
-
"allowJs": true,
|
|
9
|
-
|
|
10
|
-
// Bundler mode
|
|
11
|
-
"moduleResolution": "bundler",
|
|
12
|
-
"allowImportingTsExtensions": true,
|
|
13
|
-
"verbatimModuleSyntax": true,
|
|
14
|
-
"noEmit": true,
|
|
15
|
-
|
|
16
|
-
// Best practices
|
|
17
|
-
"strict": true,
|
|
18
|
-
"skipLibCheck": true,
|
|
19
|
-
"noFallthroughCasesInSwitch": true,
|
|
20
|
-
|
|
21
|
-
// Some stricter flags (disabled by default)
|
|
22
|
-
"noUnusedLocals": false,
|
|
23
|
-
"noUnusedParameters": false,
|
|
24
|
-
"noPropertyAccessFromIndexSignature": false
|
|
25
|
-
}
|
|
26
|
-
}
|
package/tsup.config.ts
DELETED