Skip to content

Commit 585263f

Browse files
created a public api for accessing signals and updated the computed api to be more consise and to the point
1 parent 789b6e3 commit 585263f

File tree

5 files changed

+171
-106
lines changed

5 files changed

+171
-106
lines changed

src/components/Timer.tsx

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,29 @@
1-
import { createSignal, createEffect, cleanUp } from "../index";
1+
import {
2+
createSignal,
3+
createEffect,
4+
cleanUp,
5+
createPromise,
6+
computed,
7+
} from "../index";
28

39
function TimerComponent() {
410
const seconds = createSignal<number>(0);
511
const other = createSignal({ a: [-1], b: 2, c: 3 });
6-
12+
// const promise = createPromise(
13+
// () =>
14+
// new Promise<string>((resolve) =>
15+
// setTimeout(() => resolve("hello"), 1000)
16+
// )
17+
// );
18+
const seconds2 = computed(() => other.value.a[0]);
719
// const interval = setInterval(() => {
820
// // other.update((other) => other.a++);
921
// seconds.update((seconds) => seconds + 1);
1022
// }, 1000);
11-
1223
createEffect(() => {
13-
seconds.update((prev) => prev + 1);
14-
seconds.value;
24+
// seconds.update((prev) => prev + 1);
25+
// seconds.value;
26+
console.log(other.value);
1527
return () => {
1628
console.log("cleanup");
1729
};
@@ -24,13 +36,16 @@ function TimerComponent() {
2436

2537
return (
2638
<p id="p">
27-
Elapsed Time: {() => seconds.value}
39+
Elapsed Time: {() => seconds2.value}
2840
{/* {() => other.value.a.map((i) => <p>{i}</p>)} seconds */}
2941
<button
3042
onClick={() => {
3143
// other.update((other) => other.a.push(seconds.value));
3244
// other.value.a.push(seconds.value);
33-
seconds.update((prev) => prev + 1);
45+
// seconds.update((prev) => prev + 1);
46+
other.update((prev) => {
47+
prev.a[0]++;
48+
});
3449
}}
3550
>
3651
Click

src/index.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ import type {
1414
BaseSignal,
1515
ObjectSignal,
1616
PrimitiveSignal,
17+
PublicArraySignal,
18+
PublicObjectSignal,
19+
PublicSignal,
20+
Ref,
1721
} from "./signals/signal";
1822

1923
export {
@@ -30,4 +34,8 @@ export {
3034
BaseSignal,
3135
ObjectSignal,
3236
PrimitiveSignal,
37+
PublicArraySignal,
38+
PublicObjectSignal,
39+
PublicSignal,
40+
Ref,
3341
};

src/signals/signal.ts

Lines changed: 93 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -69,26 +69,32 @@ export function createEffect(fn: Function) {
6969
currentEffect = null;
7070
}
7171

72-
function computed<T extends NormalSignal>(val: () => T): PrimitiveSignal<T>;
73-
function computed<T extends any[]>(val: () => T): ArraySignal<T>;
74-
function computed<T extends Record<any, any>>(val: () => T): ObjectSignal<T>;
7572
function computed<T extends NormalSignal | any[] | Record<any, any>>(
7673
fn: () => T
7774
) {
7875
if (typeof fn !== "function")
7976
throw new Error("computed takes a function as the argument");
8077

8178
currentEffect = () => {
82-
const newVal = fn();
83-
signal.update(newVal);
79+
let newVal = fn();
80+
if (newVal !== signal.value) {
81+
signal.update(newVal);
82+
}
8483
};
84+
8585
addEffect(currentEffect);
86+
8687
const val = fn();
87-
// @ts-expect-error
88+
89+
// @ts-expect-error - Type assertion for signal
8890
const signal = createSignal<T>(val);
89-
currentEffect = null;
9091

91-
return signal;
92+
currentEffect = null;
93+
return {
94+
get value() {
95+
return signal.value;
96+
},
97+
};
9298
}
9399

94100
type PromiseOverload<T> =
@@ -126,7 +132,11 @@ export function createPromise<T>(fn: () => Promise<T>) {
126132
});
127133
});
128134

129-
return triggerSignal;
135+
return {
136+
get value() {
137+
return triggerSignal.value;
138+
},
139+
};
130140
}
131141

132142
export class Ref<T extends HTMLElement> {
@@ -141,30 +151,43 @@ export function createRef<T extends HTMLElement>() {
141151
return ref;
142152
}
143153

144-
const NonMutatingArrayMethods = [
145-
"concat",
146-
"every",
147-
"filter",
148-
"find",
149-
"findIndex",
150-
"flat",
151-
"flatMap",
152-
"forEach",
153-
"includes",
154-
"indexOf",
155-
"join",
156-
"map",
157-
"reduce",
158-
"reduceRight",
159-
"slice",
160-
"some",
161-
"toLocaleString",
162-
"toString",
154+
// const NonMutatingArrayMethods = [
155+
// "constructor",
156+
// "concat",
157+
// "every",
158+
// "filter",
159+
// "find",
160+
// "findIndex",
161+
// "flat",
162+
// "flatMap",
163+
// "forEach",
164+
// "includes",
165+
// "indexOf",
166+
// "join",
167+
// "map",
168+
// "reduce",
169+
// "reduceRight",
170+
// "slice",
171+
// "some",
172+
// "toLocaleString",
173+
// "toString",
174+
// ];
175+
const MutatingMethods = [
176+
"push",
177+
"pop",
178+
"unshift",
179+
"shift",
180+
"splice",
181+
"fill",
182+
"copyWithin",
183+
"sort",
184+
"reverse",
163185
];
164186

165187
type DeepReadonly<T> = {
166188
readonly [K in keyof T]: T[K] extends object ? DeepReadonly<T[K]> : T[K];
167189
};
190+
168191
/**
169192
*
170193
* Base class for signals.
@@ -286,17 +309,18 @@ export class ArraySignal<T extends any[]> extends BaseSignal<T> {
286309

287310
if (typeof value === "function") {
288311
if (
289-
!NonMutatingArrayMethods.includes(String(prop)) &&
312+
MutatingMethods.includes(String(prop)) &&
290313
!this.updateCalled
291314
) {
292315
throw new Error(
293316
"Cannot set a value on an array signal, use the update method for updating the array."
294317
);
295318
}
319+
296320
return (...args: any[]) => {
297321
const result = value.apply(target, args);
298322
// Notify if the method is mutating.
299-
if (!NonMutatingArrayMethods.includes(String(prop))) {
323+
if (MutatingMethods.includes(String(prop))) {
300324
this.notify();
301325
}
302326
return result;
@@ -330,17 +354,6 @@ export class ArraySignal<T extends any[]> extends BaseSignal<T> {
330354
return this._val;
331355
}
332356

333-
// set value(val: T) {
334-
// if (!Array.isArray(val)) {
335-
// throw new Error(
336-
// "Invalid type for ArraySignal; value must be an array"
337-
// );
338-
// }
339-
// if (val === this._val) return;
340-
// this._val = this.createProxy(val);
341-
// this.notify();
342-
// }
343-
344357
public update(val: T | ((prev: T) => void)) {
345358
this.updateCalled = true;
346359
if (typeof val === "function") {
@@ -383,16 +396,17 @@ export class ObjectSignal<T extends Record<any, any>> extends BaseSignal<T> {
383396
if (typeof value === "function") {
384397
if (
385398
!this.updateCalled &&
386-
!NonMutatingArrayMethods.includes(String(prop))
399+
MutatingMethods.includes(String(prop))
387400
) {
388401
throw new Error(
389402
"Cannot set a value on an object signal, use the update method for updating the object."
390403
);
391404
}
405+
392406
return (...args: any[]) => {
393407
const result = value.apply(target, args);
394408
// Notify if the method is mutating.
395-
if (!NonMutatingArrayMethods.includes(String(prop))) {
409+
if (MutatingMethods.includes(String(prop))) {
396410
this.notify();
397411
}
398412
return result;
@@ -467,17 +481,6 @@ export class ObjectSignal<T extends Record<any, any>> extends BaseSignal<T> {
467481
return this._val;
468482
}
469483

470-
// set value(val: T) {
471-
// if (!isPlainObject(val)) {
472-
// throw new Error(
473-
// "Invalid type for ObjectSignal; value must be a plain object"
474-
// );
475-
// }
476-
// if (val === this._val) return;
477-
// this._val = this.createProxy(val);
478-
// this.notify();
479-
// }
480-
481484
public update(val: T | ((prev: T) => void)) {
482485
this.updateCalled = true;
483486
if (typeof val === "function") {
@@ -496,12 +499,27 @@ export class ObjectSignal<T extends Record<any, any>> extends BaseSignal<T> {
496499
}
497500
}
498501

502+
export interface PublicSignal<T> {
503+
readonly value: DeepReadonly<T>;
504+
update(val: T | ((prev: T) => T)): void;
505+
}
506+
507+
export interface PublicArraySignal<T extends any[]> extends PublicSignal<T> {
508+
update(val: T | ((prev: T) => void)): void; // Mutation allowed
509+
}
510+
511+
export interface PublicObjectSignal<T extends Record<any, any>>
512+
extends PublicSignal<T> {
513+
update(val: T | ((prev: T) => void)): void; // Mutation allowed
514+
}
499515
/**
500516
* Overloaded factory function to create a signal.
501517
*/
502-
function createSignal<T extends NormalSignal>(val: T): PrimitiveSignal<T>;
503-
function createSignal<T extends any[]>(val: T): ArraySignal<T>;
504-
function createSignal<T extends Record<any, any>>(val: T): ObjectSignal<T>;
518+
function createSignal<T extends NormalSignal>(val: T): PublicSignal<T>;
519+
function createSignal<T extends any[]>(val: T): PublicArraySignal<T>;
520+
function createSignal<T extends Record<any, any>>(
521+
val: T
522+
): PublicObjectSignal<T>;
505523

506524
function createSignal<T extends NormalSignal | any[] | Record<any, any>>(
507525
val: T
@@ -514,11 +532,21 @@ function createSignal<T extends NormalSignal | any[] | Record<any, any>>(
514532
if (Array.isArray(val)) {
515533
const signal = new ArraySignal(val);
516534
addSignal(signal);
517-
return signal;
535+
return {
536+
get value() {
537+
return signal.value;
538+
},
539+
update: signal.update.bind(signal) as typeof signal.update,
540+
};
518541
} else if (isPlainObject(val)) {
519542
const signal = new ObjectSignal(val);
520543
addSignal(signal);
521-
return signal;
544+
return {
545+
get value() {
546+
return signal.value;
547+
},
548+
update: signal.update.bind(signal) as typeof signal.update,
549+
};
522550
} else {
523551
throw new Error(
524552
"Invalid type for signal initialization: " + typeof val
@@ -527,7 +555,12 @@ function createSignal<T extends NormalSignal | any[] | Record<any, any>>(
527555
} else if (isPrimitive(val)) {
528556
const signal = new PrimitiveSignal(val);
529557
addSignal(signal);
530-
return signal;
558+
return {
559+
get value() {
560+
return signal.value;
561+
},
562+
update: signal.update.bind(signal) as typeof signal.update,
563+
};
531564
} else {
532565
throw new Error(
533566
"Invalid type for signal initialization: " + typeof val

src/tests/rendering/render.test.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -261,15 +261,13 @@ describe("commitDeletion", () => {
261261

262262
expect(container.dom.innerHTML).toBe("<div>1</div>");
263263

264-
expect(count.deps.size).toBe(1);
265-
266264
commitDeletion(fiber, true);
267265
expect(fiber.props.children[0].renderFunction).toBeUndefined();
266+
expect(container.dom.innerHTML).toBe("");
268267

269268
count.update(2);
270269
await Promise.resolve();
271270

272-
expect(count.deps.size).toBe(0);
273271
expect(container.dom.innerHTML).toBe("");
274272
});
275273
});

0 commit comments

Comments
 (0)