@@ -78,6 +78,7 @@ export type Capabilities = {|
78
78
export default class Store extends EventEmitter < { |
79
79
collapseNodesByDefault : [ ] ,
80
80
componentFilters : [ ] ,
81
+ error : [ Error ] ,
81
82
mutated : [ [ Array < number > , Map < number , number > ] ] ,
82
83
recordChangeDescriptions : [ ] ,
83
84
roots : [ ] ,
@@ -270,12 +271,14 @@ export default class Store extends EventEmitter<{|
270
271
assertMapSizeMatchesRootCount ( map : Map < any , any > , mapName : string ) {
271
272
const expectedSize = this . roots . length ;
272
273
if ( map . size !== expectedSize ) {
273
- throw new Error (
274
- `Expected ${ mapName } to contain ${ expectedSize } items, but it contains ${
275
- map . size
276
- } items\n\n${ inspect ( map , {
277
- depth : 20 ,
278
- } ) } `,
274
+ this . _throwAndEmitError (
275
+ Error (
276
+ `Expected ${ mapName } to contain ${ expectedSize } items, but it contains ${
277
+ map . size
278
+ } items\n\n${ inspect ( map , {
279
+ depth : 20 ,
280
+ } ) } `,
281
+ ) ,
279
282
) ;
280
283
}
281
284
}
@@ -301,7 +304,9 @@ export default class Store extends EventEmitter<{|
301
304
if ( this . _profilerStore . isProfiling ) {
302
305
// Re-mounting a tree while profiling is in progress might break a lot of assumptions.
303
306
// If necessary, we could support this- but it doesn't seem like a necessary use case.
304
- throw Error ( 'Cannot modify filter preferences while profiling' ) ;
307
+ this . _throwAndEmitError (
308
+ Error ( 'Cannot modify filter preferences while profiling' ) ,
309
+ ) ;
305
310
}
306
311
307
312
// Filter updates are expensive to apply (since they impact the entire tree).
@@ -607,7 +612,7 @@ export default class Store extends EventEmitter<{|
607
612
}
608
613
609
614
if ( depth === 0 ) {
610
- throw Error ( 'Invalid owners list' ) ;
615
+ this . _throwAndEmitError ( Error ( 'Invalid owners list' ) ) ;
611
616
}
612
617
613
618
list . push ( { ...innerElement , depth} ) ;
@@ -667,7 +672,7 @@ export default class Store extends EventEmitter<{|
667
672
if ( element !== null ) {
668
673
if ( isCollapsed ) {
669
674
if ( element . type === ElementTypeRoot ) {
670
- throw Error ( 'Root nodes cannot be collapsed' ) ;
675
+ this . _throwAndEmitError ( Error ( 'Root nodes cannot be collapsed' ) ) ;
671
676
}
672
677
673
678
if ( ! element . isCollapsed ) {
@@ -825,8 +830,10 @@ export default class Store extends EventEmitter<{|
825
830
i += 3 ;
826
831
827
832
if ( this . _idToElement . has ( id ) ) {
828
- throw Error (
829
- `Cannot add node "${ id } " because a node with that id is already in the Store.` ,
833
+ this . _throwAndEmitError (
834
+ Error (
835
+ `Cannot add node "${ id } " because a node with that id is already in the Store.` ,
836
+ ) ,
830
837
) ;
831
838
}
832
839
@@ -888,8 +895,10 @@ export default class Store extends EventEmitter<{|
888
895
}
889
896
890
897
if ( ! this . _idToElement . has ( parentID ) ) {
891
- throw Error (
892
- `Cannot add child "${ id } " to parent "${ parentID } " because parent node was not found in the Store.` ,
898
+ this . _throwAndEmitError (
899
+ Error (
900
+ `Cannot add child "${ id } " to parent "${ parentID } " because parent node was not found in the Store.` ,
901
+ ) ,
893
902
) ;
894
903
}
895
904
@@ -940,8 +949,10 @@ export default class Store extends EventEmitter<{|
940
949
const id = ( ( operations [ i ] : any ) : number ) ;
941
950
942
951
if ( ! this . _idToElement . has ( id ) ) {
943
- throw Error (
944
- `Cannot remove node "${ id } " because no matching node was found in the Store.` ,
952
+ this . _throwAndEmitError (
953
+ Error (
954
+ `Cannot remove node "${ id } " because no matching node was found in the Store.` ,
955
+ ) ,
945
956
) ;
946
957
}
947
958
@@ -950,7 +961,9 @@ export default class Store extends EventEmitter<{|
950
961
const element = ( ( this . _idToElement . get ( id ) : any ) : Element ) ;
951
962
const { children, ownerID, parentID, weight} = element ;
952
963
if ( children . length > 0 ) {
953
- throw new Error ( `Node "${ id } " was removed before its children.` ) ;
964
+ this . _throwAndEmitError (
965
+ Error ( `Node "${ id } " was removed before its children.` ) ,
966
+ ) ;
954
967
}
955
968
956
969
this . _idToElement . delete ( id ) ;
@@ -972,8 +985,10 @@ export default class Store extends EventEmitter<{|
972
985
}
973
986
parentElement = ( ( this . _idToElement . get ( parentID ) : any ) : Element ) ;
974
987
if ( parentElement === undefined ) {
975
- throw Error (
976
- `Cannot remove node "${ id } " from parent "${ parentID } " because no matching node was found in the Store.` ,
988
+ this . _throwAndEmitError (
989
+ Error (
990
+ `Cannot remove node "${ id } " from parent "${ parentID } " because no matching node was found in the Store.` ,
991
+ ) ,
977
992
) ;
978
993
}
979
994
const index = parentElement . children . indexOf ( id ) ;
@@ -1033,16 +1048,20 @@ export default class Store extends EventEmitter<{|
1033
1048
i += 3 ;
1034
1049
1035
1050
if ( ! this . _idToElement . has ( id ) ) {
1036
- throw Error (
1037
- `Cannot reorder children for node "${ id } " because no matching node was found in the Store.` ,
1051
+ this . _throwAndEmitError (
1052
+ Error (
1053
+ `Cannot reorder children for node "${ id } " because no matching node was found in the Store.` ,
1054
+ ) ,
1038
1055
) ;
1039
1056
}
1040
1057
1041
1058
const element = ( ( this . _idToElement . get ( id ) : any ) : Element ) ;
1042
1059
const children = element . children ;
1043
1060
if ( children . length !== numChildren ) {
1044
- throw Error (
1045
- `Children cannot be added or removed during a reorder operation.` ,
1061
+ this . _throwAndEmitError (
1062
+ Error (
1063
+ `Children cannot be added or removed during a reorder operation.` ,
1064
+ ) ,
1046
1065
) ;
1047
1066
}
1048
1067
@@ -1087,7 +1106,9 @@ export default class Store extends EventEmitter<{|
1087
1106
haveErrorsOrWarningsChanged = true ;
1088
1107
break ;
1089
1108
default :
1090
- throw Error ( `Unsupported Bridge operation "${ operation } "` ) ;
1109
+ this . _throwAndEmitError (
1110
+ Error ( `Unsupported Bridge operation "${ operation } "` ) ,
1111
+ ) ;
1091
1112
}
1092
1113
}
1093
1114
@@ -1251,4 +1272,17 @@ export default class Store extends EventEmitter<{|
1251
1272
1252
1273
this . emit ( 'unsupportedBridgeProtocolDetected' ) ;
1253
1274
} ;
1275
+
1276
+ // The Store should never throw an Error without also emitting an event.
1277
+ // Otherwise Store errors will be invisible to users,
1278
+ // but the downstream errors they cause will be reported as bugs.
1279
+ // For example, https://github.com/facebook/react/issues/21402
1280
+ // Emitting an error event allows the ErrorBoundary to show the original error.
1281
+ _throwAndEmitError ( error : Error ) {
1282
+ this . emit ( 'error' , error ) ;
1283
+
1284
+ // Throwing is still valuable for local development
1285
+ // and for unit testing the Store itself.
1286
+ throw error ;
1287
+ }
1254
1288
}
0 commit comments