11
11
*/
12
12
13
13
import { Key } from '@react-types/shared' ;
14
- import { useMemo , useState } from 'react' ;
14
+ import { useState } from 'react' ;
15
15
16
16
export interface TreeOptions < T extends object > {
17
17
/** Initial root items in the tree. */
@@ -126,44 +126,47 @@ export function useTreeData<T extends object>(options: TreeOptions<T>): TreeData
126
126
getKey = ( item : any ) => item . id || item . key ,
127
127
getChildren = ( item : any ) => item . children
128
128
} = options ;
129
- let map = useMemo ( ( ) => new Map < Key , TreeNode < T > > ( ) , [ ] ) ;
130
129
131
130
// We only want to compute this on initial render.
132
- // eslint-disable-next-line react-hooks/exhaustive-deps
133
- let initialNodes = useMemo ( ( ) => buildTree ( initialItems ) , [ ] ) ;
134
- let [ items , setItems ] = useState ( initialNodes ) ;
131
+ let [ tree , setItems ] = useState < { items : TreeNode < T > [ ] , nodeMap : Map < Key , TreeNode < T > > } > ( ( ) => buildTree ( initialItems , new Map ( ) ) ) ;
132
+ let { items , nodeMap } = tree ;
133
+
135
134
let [ selectedKeys , setSelectedKeys ] = useState ( new Set < Key > ( initialSelectedKeys || [ ] ) ) ;
136
135
137
- function buildTree ( initialItems : T [ ] = [ ] , parentKey ?: Key | null ) {
138
- return initialItems . map ( item => {
139
- let node : TreeNode < T > = {
140
- key : getKey ( item ) ,
141
- parentKey : parentKey ,
142
- value : item ,
143
- children : null
144
- } ;
136
+ function buildTree ( initialItems : T [ ] = [ ] , map : Map < Key , TreeNode < T > > , parentKey ?: Key | null ) {
137
+ return {
138
+ items : initialItems . map ( item => {
139
+ let node : TreeNode < T > = {
140
+ key : getKey ( item ) ,
141
+ parentKey : parentKey ,
142
+ value : item ,
143
+ children : null
144
+ } ;
145
145
146
- node . children = buildTree ( getChildren ( item ) , node . key ) ;
147
- map . set ( node . key , node ) ;
148
- return node ;
149
- } ) ;
146
+ node . children = buildTree ( getChildren ( item ) , map , node . key ) . items ;
147
+ map . set ( node . key , node ) ;
148
+ return node ;
149
+ } ) ,
150
+ nodeMap : map
151
+ } ;
150
152
}
151
153
152
- function updateTree ( items : TreeNode < T > [ ] , key : Key , update : ( node : TreeNode < T > ) => TreeNode < T > ) {
153
- let node = map . get ( key ) ;
154
+ function updateTree ( items : TreeNode < T > [ ] , key : Key , update : ( node : TreeNode < T > ) => TreeNode < T > , originalMap : Map < Key , TreeNode < T > > ) {
155
+ let node = originalMap . get ( key ) ;
154
156
if ( ! node ) {
155
- return items ;
157
+ return { items, nodeMap : originalMap } ;
156
158
}
159
+ let map = new Map < Key , TreeNode < T > > ( originalMap ) ;
157
160
158
161
// Create a new node. If null, then delete the node, otherwise replace.
159
162
let newNode = update ( node ) ;
160
163
if ( newNode == null ) {
161
- deleteNode ( node ) ;
164
+ deleteNode ( node , map ) ;
162
165
} else {
163
- addNode ( newNode ) ;
166
+ addNode ( newNode , map ) ;
164
167
}
165
168
166
- // Walk up the tree and update each parent to refer to the new chilren .
169
+ // Walk up the tree and update each parent to refer to the new children .
167
170
while ( node . parentKey ) {
168
171
let nextParent = map . get ( node . parentKey ) ;
169
172
let copy : TreeNode < T > = {
@@ -196,26 +199,29 @@ export function useTreeData<T extends object>(options: TreeOptions<T>): TreeData
196
199
items = items . filter ( c => c !== node ) ;
197
200
}
198
201
199
- return items . map ( item => {
200
- if ( item === node ) {
201
- return newNode ;
202
- }
202
+ return {
203
+ items : items . map ( item => {
204
+ if ( item === node ) {
205
+ return newNode ;
206
+ }
203
207
204
- return item ;
205
- } ) ;
208
+ return item ;
209
+ } ) ,
210
+ nodeMap : map
211
+ } ;
206
212
}
207
213
208
- function addNode ( node : TreeNode < T > ) {
214
+ function addNode ( node : TreeNode < T > , map : Map < Key , TreeNode < T > > ) {
209
215
map . set ( node . key , node ) ;
210
216
for ( let child of node . children ) {
211
- addNode ( child ) ;
217
+ addNode ( child , map ) ;
212
218
}
213
219
}
214
220
215
- function deleteNode ( node : TreeNode < T > ) {
221
+ function deleteNode ( node : TreeNode < T > , map : Map < Key , TreeNode < T > > ) {
216
222
map . delete ( node . key ) ;
217
223
for ( let child of node . children ) {
218
- deleteNode ( child ) ;
224
+ deleteNode ( child , map ) ;
219
225
}
220
226
}
221
227
@@ -224,19 +230,22 @@ export function useTreeData<T extends object>(options: TreeOptions<T>): TreeData
224
230
selectedKeys,
225
231
setSelectedKeys,
226
232
getItem ( key : Key ) {
227
- return map . get ( key ) ;
233
+ return nodeMap . get ( key ) ;
228
234
} ,
229
235
insert ( parentKey : Key | null , index : number , ...values : T [ ] ) {
230
- setItems ( items => {
231
- let nodes = buildTree ( values , parentKey ) ;
236
+ setItems ( ( { items, nodeMap : originalMap } ) => {
237
+ let { items : newNodes , nodeMap : newMap } = buildTree ( values , originalMap , parentKey ) ;
232
238
233
239
// If parentKey is null, insert into the root.
234
240
if ( parentKey == null ) {
235
- return [
236
- ...items . slice ( 0 , index ) ,
237
- ...nodes ,
238
- ...items . slice ( index )
239
- ] ;
241
+ return {
242
+ items : [
243
+ ...items . slice ( 0 , index ) ,
244
+ ...newNodes ,
245
+ ...items . slice ( index )
246
+ ] ,
247
+ nodeMap : newMap
248
+ } ;
240
249
}
241
250
242
251
// Otherwise, update the parent node and its ancestors.
@@ -246,30 +255,30 @@ export function useTreeData<T extends object>(options: TreeOptions<T>): TreeData
246
255
value : parentNode . value ,
247
256
children : [
248
257
...parentNode . children . slice ( 0 , index ) ,
249
- ...nodes ,
258
+ ...newNodes ,
250
259
...parentNode . children . slice ( index )
251
260
]
252
- } ) ) ;
261
+ } ) , newMap ) ;
253
262
} ) ;
254
263
} ,
255
264
insertBefore ( key : Key , ...values : T [ ] ) : void {
256
- let node = map . get ( key ) ;
265
+ let node = nodeMap . get ( key ) ;
257
266
if ( ! node ) {
258
267
return ;
259
268
}
260
269
261
- let parentNode = map . get ( node . parentKey ) ;
270
+ let parentNode = nodeMap . get ( node . parentKey ) ;
262
271
let nodes = parentNode ? parentNode . children : items ;
263
272
let index = nodes . indexOf ( node ) ;
264
273
this . insert ( parentNode ?. key , index , ...values ) ;
265
274
} ,
266
275
insertAfter ( key : Key , ...values : T [ ] ) : void {
267
- let node = map . get ( key ) ;
276
+ let node = nodeMap . get ( key ) ;
268
277
if ( ! node ) {
269
278
return ;
270
279
}
271
280
272
- let parentNode = map . get ( node . parentKey ) ;
281
+ let parentNode = nodeMap . get ( node . parentKey ) ;
273
282
let nodes = parentNode ? parentNode . children : items ;
274
283
let index = nodes . indexOf ( node ) ;
275
284
this . insert ( parentNode ?. key , index + 1 , ...values ) ;
@@ -281,7 +290,7 @@ export function useTreeData<T extends object>(options: TreeOptions<T>): TreeData
281
290
if ( parentKey == null ) {
282
291
this . insert ( null , items . length , ...values ) ;
283
292
} else {
284
- let parentNode = map . get ( parentKey ) ;
293
+ let parentNode = nodeMap . get ( parentKey ) ;
285
294
if ( ! parentNode ) {
286
295
return ;
287
296
}
@@ -290,16 +299,24 @@ export function useTreeData<T extends object>(options: TreeOptions<T>): TreeData
290
299
}
291
300
} ,
292
301
remove ( ...keys : Key [ ] ) {
302
+ if ( keys . length === 0 ) {
303
+ return ;
304
+ }
305
+
293
306
let newItems = items ;
307
+ let prevMap = nodeMap ;
308
+ let newTree ;
294
309
for ( let key of keys ) {
295
- newItems = updateTree ( newItems , key , ( ) => null ) ;
310
+ newTree = updateTree ( newItems , key , ( ) => null , prevMap ) ;
311
+ prevMap = newTree . nodeMap ;
312
+ newItems = newTree . items ;
296
313
}
297
314
298
- setItems ( newItems ) ;
315
+ setItems ( newTree ) ;
299
316
300
317
let selection = new Set ( selectedKeys ) ;
301
318
for ( let key of selectedKeys ) {
302
- if ( ! map . has ( key ) ) {
319
+ if ( ! newTree . nodeMap . has ( key ) ) {
303
320
selection . delete ( key ) ;
304
321
}
305
322
}
@@ -310,13 +327,14 @@ export function useTreeData<T extends object>(options: TreeOptions<T>): TreeData
310
327
this . remove ( ...selectedKeys ) ;
311
328
} ,
312
329
move ( key : Key , toParentKey : Key | null , index : number ) {
313
- setItems ( items => {
314
- let node = map . get ( key ) ;
330
+ setItems ( ( { items, nodeMap : originalMap } ) => {
331
+ let node = originalMap . get ( key ) ;
315
332
if ( ! node ) {
316
- return items ;
333
+ return { items, nodeMap : originalMap } ;
317
334
}
318
335
319
- items = updateTree ( items , key , ( ) => null ) ;
336
+ let { items : newItems , nodeMap : newMap } = updateTree ( items , key , ( ) => null , originalMap ) ;
337
+
320
338
321
339
const movedNode = {
322
340
...node ,
@@ -325,15 +343,15 @@ export function useTreeData<T extends object>(options: TreeOptions<T>): TreeData
325
343
326
344
// If parentKey is null, insert into the root.
327
345
if ( toParentKey == null ) {
328
- return [
329
- ...items . slice ( 0 , index ) ,
346
+ return { items : [
347
+ ...newItems . slice ( 0 , index ) ,
330
348
movedNode ,
331
- ...items . slice ( index )
332
- ] ;
349
+ ...newItems . slice ( index )
350
+ ] , nodeMap : newMap } ;
333
351
}
334
352
335
353
// Otherwise, update the parent node and its ancestors.
336
- return updateTree ( items , toParentKey , parentNode => ( {
354
+ return updateTree ( newItems , toParentKey , parentNode => ( {
337
355
key : parentNode . key ,
338
356
parentKey : parentNode . parentKey ,
339
357
value : parentNode . value ,
@@ -342,21 +360,22 @@ export function useTreeData<T extends object>(options: TreeOptions<T>): TreeData
342
360
movedNode ,
343
361
...parentNode . children . slice ( index )
344
362
]
345
- } ) ) ;
363
+ } ) , newMap ) ;
346
364
} ) ;
347
365
} ,
348
366
update ( oldKey : Key , newValue : T ) {
349
- setItems ( items => updateTree ( items , oldKey , oldNode => {
367
+ setItems ( ( { items, nodeMap : originalMap } ) => updateTree ( items , oldKey , oldNode => {
350
368
let node : TreeNode < T > = {
351
369
key : oldNode . key ,
352
370
parentKey : oldNode . parentKey ,
353
371
value : newValue ,
354
372
children : null
355
373
} ;
356
374
357
- node . children = buildTree ( getChildren ( newValue ) , node . key ) ;
375
+ let tree = buildTree ( getChildren ( newValue ) , originalMap , node . key ) ;
376
+ node . children = tree . items ;
358
377
return node ;
359
- } ) ) ;
378
+ } , originalMap ) ) ;
360
379
}
361
380
} ;
362
381
}
0 commit comments