11import { Accessor , batch } from "solid-js" ;
22import { TriggerCache } from "@solid-primitives/trigger" ;
33
4- const $KEYS = Symbol ( "track-keys " ) ;
4+ const $OBJECT = Symbol ( "track-object " ) ;
55
66/**
77 * A reactive version of `Map` data structure. All the reads (like `get` or `has`) are signals, and all the writes (`delete` or `set`) will cause updates to appropriate signals.
@@ -21,100 +21,114 @@ const $KEYS = Symbol("track-keys");
2121 * userPoints.set(user1, { foo: "bar" });
2222 */
2323export class ReactiveMap < K , V > extends Map < K , V > {
24- #keyTriggers = new TriggerCache < K | typeof $KEYS > ( ) ;
25- #valueTriggers = new TriggerCache < K > ( ) ;
24+ #keyTriggers = new TriggerCache < K | typeof $OBJECT > ( ) ;
25+ #valueTriggers = new TriggerCache < K | typeof $OBJECT > ( ) ;
2626
27- constructor ( initial ?: Iterable < readonly [ K , V ] > | null ) {
28- super ( ) ;
29- if ( initial ) for ( const v of initial ) super . set ( v [ 0 ] , v [ 1 ] ) ;
27+ [ Symbol . iterator ] ( ) : MapIterator < [ K , V ] > {
28+ return this . entries ( ) ;
3029 }
3130
32- // reads
33- has ( key : K ) : boolean {
34- this . #keyTriggers. track ( key ) ;
35- return super . has ( key ) ;
36- }
37- get ( key : K ) : V | undefined {
38- this . #valueTriggers. track ( key ) ;
39- return super . get ( key ) ;
31+ constructor ( entries ?: Iterable < readonly [ K , V ] > | null ) {
32+ super ( ) ;
33+ if ( entries ) for ( const entry of entries ) super . set ( ...entry ) ;
4034 }
35+
4136 get size ( ) : number {
42- this . #keyTriggers. track ( $KEYS ) ;
37+ this . #keyTriggers. track ( $OBJECT ) ;
4338 return super . size ;
4439 }
4540
4641 * keys ( ) : MapIterator < K > {
42+ this . #keyTriggers. track ( $OBJECT ) ;
43+
4744 for ( const key of super . keys ( ) ) {
48- this . #keyTriggers. track ( key ) ;
4945 yield key ;
5046 }
51- this . #keyTriggers. track ( $KEYS ) ;
5247 }
48+
5349 * values ( ) : MapIterator < V > {
54- for ( const [ key , v ] of super . entries ( ) ) {
55- this . #valueTriggers. track ( key ) ;
56- yield v ;
50+ this . #valueTriggers. track ( $OBJECT ) ;
51+
52+ for ( const value of super . values ( ) ) {
53+ yield value ;
5754 }
58- this . #keyTriggers. track ( $KEYS ) ;
5955 }
56+
6057 * entries ( ) : MapIterator < [ K , V ] > {
58+ this . #keyTriggers. track ( $OBJECT ) ;
59+ this . #valueTriggers. track ( $OBJECT ) ;
60+
6161 for ( const entry of super . entries ( ) ) {
62- this . #valueTriggers. track ( entry [ 0 ] ) ;
6362 yield entry ;
6463 }
65- this . #keyTriggers. track ( $KEYS ) ;
6664 }
6765
68- // writes
66+ forEach ( callbackfn : ( value : V , key : K , map : Map < K , V > ) => void , thisArg ?: any ) : void {
67+ this . #keyTriggers. track ( $OBJECT ) ;
68+ this . #valueTriggers. track ( $OBJECT ) ;
69+ super . forEach ( callbackfn , thisArg ) ;
70+ }
71+
72+ has ( key : K ) : boolean {
73+ this . #keyTriggers. track ( key ) ;
74+ return super . has ( key ) ;
75+ }
76+
77+ get ( key : K ) : V | undefined {
78+ this . #valueTriggers. track ( key ) ;
79+ return super . get ( key ) ;
80+ }
81+
6982 set ( key : K , value : V ) : this {
70- batch ( ( ) => {
71- if ( super . has ( key ) ) {
72- if ( super . get ( key ) ! === value ) return ;
73- } else {
74- this . #keyTriggers. dirty ( key ) ;
75- this . #keyTriggers. dirty ( $KEYS ) ;
76- }
77- this . #valueTriggers. dirty ( key ) ;
78- super . set ( key , value ) ;
79- } ) ;
80- return this ;
83+ const hadNoKey = ! super . has ( key ) ;
84+ const hasChanged = super . get ( key ) !== value ;
85+ const result = super . set ( key , value ) ;
86+
87+ if ( hasChanged || hadNoKey ) {
88+ batch ( ( ) => {
89+ if ( hadNoKey ) {
90+ this . #keyTriggers. dirty ( $OBJECT ) ;
91+ this . #keyTriggers. dirty ( key ) ;
92+ }
93+ if ( hasChanged ) {
94+ this . #valueTriggers. dirty ( $OBJECT ) ;
95+ this . #valueTriggers. dirty ( key ) ;
96+ }
97+ } ) ;
98+ }
99+
100+ return result ;
81101 }
102+
82103 delete ( key : K ) : boolean {
83- const r = super . delete ( key ) ;
84- if ( r ) {
104+ const isDefined = super . get ( key ) !== undefined ;
105+ const result = super . delete ( key ) ;
106+
107+ if ( result ) {
85108 batch ( ( ) => {
109+ this . #keyTriggers. dirty ( $OBJECT ) ;
110+ this . #valueTriggers. dirty ( $OBJECT ) ;
86111 this . #keyTriggers. dirty ( key ) ;
87- this . #keyTriggers. dirty ( $KEYS ) ;
88- this . #valueTriggers. dirty ( key ) ;
112+
113+ if ( isDefined ) {
114+ this . #valueTriggers. dirty ( key ) ;
115+ }
89116 } ) ;
90117 }
91- return r ;
118+
119+ return result ;
92120 }
121+
93122 clear ( ) : void {
94123 if ( super . size ) {
124+ super . clear ( ) ;
125+
95126 batch ( ( ) => {
96- for ( const v of super . keys ( ) ) {
97- this . #keyTriggers. dirty ( v ) ;
98- this . #valueTriggers. dirty ( v ) ;
99- }
100- super . clear ( ) ;
101- this . #keyTriggers. dirty ( $KEYS ) ;
127+ this . #keyTriggers. dirtyAll ( ) ;
128+ this . #valueTriggers. dirtyAll ( ) ;
102129 } ) ;
103130 }
104131 }
105-
106- // callback
107- forEach ( callbackfn : ( value : V , key : K , map : this) => void ) {
108- this . #keyTriggers. track ( $KEYS ) ;
109- for ( const [ key , v ] of super . entries ( ) ) {
110- this . #valueTriggers. track ( key ) ;
111- callbackfn ( v , key , this ) ;
112- }
113- }
114-
115- [ Symbol . iterator ] ( ) : MapIterator < [ K , V ] > {
116- return this . entries ( ) ;
117- }
118132}
119133
120134/**
@@ -137,9 +151,9 @@ export class ReactiveWeakMap<K extends object, V> extends WeakMap<K, V> {
137151 #keyTriggers = new TriggerCache < K > ( WeakMap ) ;
138152 #valueTriggers = new TriggerCache < K > ( WeakMap ) ;
139153
140- constructor ( initial ?: Iterable < readonly [ K , V ] > | null ) {
154+ constructor ( entries ?: Iterable < readonly [ K , V ] > | null ) {
141155 super ( ) ;
142- if ( initial ) for ( const v of initial ) super . set ( v [ 0 ] , v [ 1 ] ) ;
156+ if ( entries ) for ( const entry of entries ) super . set ( ... entry ) ;
143157 }
144158
145159 has ( key : K ) : boolean {
@@ -151,24 +165,31 @@ export class ReactiveWeakMap<K extends object, V> extends WeakMap<K, V> {
151165 return super . get ( key ) ;
152166 }
153167 set ( key : K , value : V ) : this {
154- batch ( ( ) => {
155- if ( super . has ( key ) ) {
156- if ( super . get ( key ) ! === value ) return ;
157- } else this . #keyTriggers. dirty ( key ) ;
158- this . #valueTriggers. dirty ( key ) ;
159- super . set ( key , value ) ;
160- } ) ;
161- return this ;
168+ const hadNoKey = ! super . has ( key ) ;
169+ const hasChanged = super . get ( key ) !== value ;
170+ const result = super . set ( key , value ) ;
171+
172+ if ( hasChanged || hadNoKey ) {
173+ batch ( ( ) => {
174+ if ( hadNoKey ) this . #keyTriggers. dirty ( key ) ;
175+ if ( hasChanged ) this . #valueTriggers. dirty ( key ) ;
176+ } ) ;
177+ }
178+
179+ return result ;
162180 }
163181 delete ( key : K ) : boolean {
164- const r = super . delete ( key ) ;
165- if ( r ) {
182+ const isDefined = super . get ( key ) !== undefined ;
183+ const result = super . delete ( key ) ;
184+
185+ if ( result ) {
166186 batch ( ( ) => {
167187 this . #keyTriggers. dirty ( key ) ;
168- this . #valueTriggers. dirty ( key ) ;
188+ if ( isDefined ) this . #valueTriggers. dirty ( key ) ;
169189 } ) ;
170190 }
171- return r ;
191+
192+ return result ;
172193 }
173194}
174195
0 commit comments