Skip to content

Commit 21e40c8

Browse files
refactor registration callback into separate class
1 parent f0649cf commit 21e40c8

File tree

4 files changed

+88
-105
lines changed

4 files changed

+88
-105
lines changed
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { ItemRegistrationCallback } from "./types";
2+
3+
export default class CallbackRegistry<T> {
4+
private registeredItems = new Map<string, T>();
5+
private callbacks = new Map<string, Array<ItemRegistrationCallback<T>>>();
6+
7+
set(name: string, item: T): void {
8+
this.registeredItems.set(name, item);
9+
10+
const callbacks = this.callbacks.get(name) || [];
11+
callbacks.forEach(callback => {
12+
setTimeout(() => callback(item), 0);
13+
});
14+
this.callbacks.delete(name);
15+
}
16+
17+
get(name: string): T | undefined {
18+
return this.registeredItems.get(name);
19+
}
20+
21+
has(name: string): boolean {
22+
return this.registeredItems.has(name);
23+
}
24+
25+
clear(): void {
26+
this.registeredItems.clear();
27+
}
28+
29+
getAll(): Map<string, T> {
30+
return this.registeredItems;
31+
}
32+
33+
onItemRegistered(name: string, callback: ItemRegistrationCallback<T>): void {
34+
const existingItem = this.registeredItems.get(name);
35+
if (existingItem) {
36+
setTimeout(() => callback(existingItem), 0);
37+
return;
38+
}
39+
40+
const callbacks = this.callbacks.get(name) || [];
41+
callbacks.push(callback);
42+
this.callbacks.set(name, callbacks);
43+
}
44+
45+
getOrWaitForItem(name: string): Promise<T> {
46+
return new Promise((resolve) => {
47+
this.onItemRegistered(name, resolve);
48+
});
49+
}
50+
}

node_package/src/ComponentRegistry.ts

Lines changed: 13 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@ import {
33
type RegisteredComponent,
44
type ReactComponentOrRenderFunction,
55
type RenderFunction,
6-
type ComponentRegistrationCallback,
6+
type ItemRegistrationCallback,
77
} from './types';
88
import isRenderFunction from './isRenderFunction';
9+
import CallbackRegistry from './CallbackRegistry';
910

10-
const registeredComponents = new Map<string, RegisteredComponent>();
11-
const registrationCallbacks = new Map<string, Array<ComponentRegistrationCallback>>();
11+
const componentRegistry = new CallbackRegistry<RegisteredComponent>();
1212

1313
export default {
1414
/**
@@ -18,27 +18,17 @@ export default {
1818
*/
1919
onComponentRegistered(
2020
componentName: string,
21-
callback: (component: RegisteredComponent) => void,
21+
callback: ItemRegistrationCallback<RegisteredComponent>,
2222
): void {
23-
// If component is already registered, schedule callback
24-
const existingComponent = registeredComponents.get(componentName);
25-
if (existingComponent) {
26-
setTimeout(() => callback(existingComponent), 0);
27-
return;
28-
}
29-
30-
// Store callback for future registration
31-
const callbacks = registrationCallbacks.get(componentName) || [];
32-
callbacks.push(callback);
33-
registrationCallbacks.set(componentName, callbacks);
23+
componentRegistry.onItemRegistered(componentName, callback);
3424
},
3525

3626
/**
3727
* @param components { component1: component1, component2: component2, etc. }
3828
*/
3929
register(components: { [id: string]: ReactComponentOrRenderFunction }): void {
4030
Object.keys(components).forEach(name => {
41-
if (registeredComponents.has(name)) {
31+
if (componentRegistry.has(name)) {
4232
console.warn('Called register for component that is already registered', name);
4333
}
4434

@@ -50,19 +40,12 @@ export default {
5040
const renderFunction = isRenderFunction(component);
5141
const isRenderer = renderFunction && (component as RenderFunction).length === 3;
5242

53-
const registeredComponent = {
43+
componentRegistry.set(name, {
5444
name,
5545
component,
5646
renderFunction,
5747
isRenderer,
58-
};
59-
registeredComponents.set(name, registeredComponent);
60-
61-
const callbacks = registrationCallbacks.get(name) || [];
62-
callbacks.forEach(callback => {
63-
setTimeout(() => callback(registeredComponent), 0);
6448
});
65-
registrationCallbacks.delete(name);
6649
});
6750
},
6851

@@ -82,20 +65,16 @@ export default {
8265
* @returns { name, component, isRenderFunction, isRenderer }
8366
*/
8467
get(name: string): RegisteredComponent {
85-
const registeredComponent = registeredComponents.get(name);
86-
if (registeredComponent !== undefined) {
87-
return registeredComponent;
88-
}
68+
const component = componentRegistry.get(name);
69+
if (component !== undefined) return component;
8970

90-
const keys = Array.from(registeredComponents.keys()).join(', ');
71+
const keys = Array.from(componentRegistry.getAll().keys()).join(', ');
9172
throw new Error(`Could not find component registered with name ${name}. \
9273
Registered component names include [ ${keys} ]. Maybe you forgot to register the component?`);
9374
},
9475

95-
async getOrWaitForComponent(name: string): Promise<RegisteredComponent> {
96-
return new Promise((resolve) => {
97-
this.onComponentRegistered(name, resolve);
98-
});
76+
getOrWaitForComponent(name: string): Promise<RegisteredComponent> {
77+
return componentRegistry.getOrWaitForItem(name);
9978
},
10079

10180
/**
@@ -104,6 +83,6 @@ Registered component names include [ ${keys} ]. Maybe you forgot to register the
10483
* { name, component, renderFunction, isRenderer}
10584
*/
10685
components(): Map<string, RegisteredComponent> {
107-
return registeredComponents;
86+
return componentRegistry.getAll();
10887
},
10988
};

node_package/src/StoreRegistry.ts

Lines changed: 24 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
1-
import type { Store, StoreGenerator } from './types';
1+
import CallbackRegistry from './CallbackRegistry';
2+
import type { Store, StoreGenerator, ItemRegistrationCallback } from './types';
23

3-
const registeredStoreGenerators = new Map<string, StoreGenerator>();
4-
const hydratedStores = new Map<string, Store>();
5-
const hydrationCallbacks = new Map<string, Array<(store: Store) => void>>();
6-
const generatorCallbacks = new Map<string, Array<(generator: StoreGenerator) => void>>();
4+
const storeGeneratorRegistry = new CallbackRegistry<StoreGenerator>();
5+
const hydratedStoreRegistry = new CallbackRegistry<Store>();
76

87
export default {
98
/**
@@ -12,7 +11,7 @@ export default {
1211
*/
1312
register(storeGenerators: { [id: string]: StoreGenerator }): void {
1413
Object.keys(storeGenerators).forEach(name => {
15-
if (registeredStoreGenerators.has(name)) {
14+
if (storeGeneratorRegistry.has(name)) {
1615
console.warn('Called registerStore for store that is already registered', name);
1716
}
1817

@@ -22,13 +21,7 @@ export default {
2221
`for the store generator with key ${name}.`);
2322
}
2423

25-
registeredStoreGenerators.set(name, store);
26-
27-
const callbacks = generatorCallbacks.get(name) || [];
28-
callbacks.forEach(callback => {
29-
setTimeout(() => callback(store), 0);
30-
});
31-
generatorCallbacks.delete(name);
24+
storeGeneratorRegistry.set(name, store);
3225
});
3326
},
3427

@@ -40,11 +33,10 @@ export default {
4033
* @returns Redux Store, possibly hydrated
4134
*/
4235
getStore(name: string, throwIfMissing = true): Store | undefined {
43-
if (hydratedStores.has(name)) {
44-
return hydratedStores.get(name);
45-
}
36+
const store = hydratedStoreRegistry.get(name);
37+
if (store) return store;
4638

47-
const storeKeys = Array.from(hydratedStores.keys()).join(', ');
39+
const storeKeys = Array.from(hydratedStoreRegistry.getAll().keys()).join(', ');
4840

4941
if (storeKeys.length === 0) {
5042
const msg =
@@ -71,12 +63,10 @@ This can happen if you are server rendering and either:
7163
* @returns storeCreator with given name
7264
*/
7365
getStoreGenerator(name: string): StoreGenerator {
74-
const registeredStoreGenerator = registeredStoreGenerators.get(name);
75-
if (registeredStoreGenerator) {
76-
return registeredStoreGenerator;
77-
}
66+
const generator = storeGeneratorRegistry.get(name);
67+
if (generator) return generator;
7868

79-
const storeKeys = Array.from(registeredStoreGenerators.keys()).join(', ');
69+
const storeKeys = Array.from(storeGeneratorRegistry.getAll().keys()).join(', ');
8070
throw new Error(`Could not find store registered with name '${name}'. Registered store ` +
8171
`names include [ ${storeKeys} ]. Maybe you forgot to register the store?`);
8272
},
@@ -87,101 +77,65 @@ This can happen if you are server rendering and either:
8777
* @param store (not the storeGenerator, but the hydrated store)
8878
*/
8979
setStore(name: string, store: Store): void {
90-
hydratedStores.set(name, store);
91-
92-
const callbacks = hydrationCallbacks.get(name) || [];
93-
callbacks.forEach(callback => {
94-
setTimeout(() => callback(store), 0);
95-
});
96-
hydrationCallbacks.delete(name);
80+
hydratedStoreRegistry.set(name, store);
9781
},
9882

9983
/**
10084
* Internally used function to completely clear hydratedStores Map.
10185
*/
10286
clearHydratedStores(): void {
103-
hydratedStores.clear();
87+
hydratedStoreRegistry.clear();
10488
},
10589

10690
/**
10791
* Get a Map containing all registered store generators. Useful for debugging.
10892
* @returns Map where key is the component name and values are the store generators.
10993
*/
11094
storeGenerators(): Map<string, StoreGenerator> {
111-
return registeredStoreGenerators;
95+
return storeGeneratorRegistry.getAll();
11296
},
11397

11498
/**
11599
* Get a Map containing all hydrated stores. Useful for debugging.
116100
* @returns Map where key is the component name and values are the hydrated stores.
117101
*/
118102
stores(): Map<string, Store> {
119-
return hydratedStores;
103+
return hydratedStoreRegistry.getAll();
120104
},
121105

122106
/**
123107
* Register a callback to be called when a specific store is hydrated
124108
* @param storeName Name of the store to watch for
125109
* @param callback Function called with the store when hydrated
126110
*/
127-
onStoreHydrated(
128-
storeName: string,
129-
callback: (store: Store) => void
130-
): void {
131-
// If store is already hydrated, schedule callback
132-
const existingStore = hydratedStores.get(storeName);
133-
if (existingStore) {
134-
setTimeout(() => callback(existingStore), 0);
135-
return;
136-
}
137-
138-
// Store callback for future hydration
139-
const callbacks = hydrationCallbacks.get(storeName) || [];
140-
callbacks.push(callback);
141-
hydrationCallbacks.set(storeName, callbacks);
111+
onStoreHydrated(storeName: string, callback: ItemRegistrationCallback<Store>): void {
112+
hydratedStoreRegistry.onItemRegistered(storeName, callback);
142113
},
143114

144115
/**
145116
* Used by components to get the hydrated store, waiting for it to be hydrated if necessary.
146117
* @param name Name of the store to wait for
147118
* @returns Promise that resolves with the Store once hydrated
148119
*/
149-
async getOrWaitForStore(name: string): Promise<Store> {
150-
return new Promise((resolve) => {
151-
this.onStoreHydrated(name, resolve);
152-
});
120+
getOrWaitForStore(name: string): Promise<Store> {
121+
return hydratedStoreRegistry.getOrWaitForItem(name);
153122
},
154123

155124
/**
156125
* Register a callback to be called when a specific store generator is registered
157126
* @param storeName Name of the store generator to watch for
158127
* @param callback Function called with the store generator when registered
159128
*/
160-
onStoreGeneratorRegistered(
161-
storeName: string,
162-
callback: (generator: StoreGenerator) => void
163-
): void {
164-
// If generator is already registered, schedule callback
165-
const existingGenerator = registeredStoreGenerators.get(storeName);
166-
if (existingGenerator) {
167-
setTimeout(() => callback(existingGenerator), 0);
168-
return;
169-
}
170-
171-
// Store callback for future registration
172-
const callbacks = generatorCallbacks.get(storeName) || [];
173-
callbacks.push(callback);
174-
generatorCallbacks.set(storeName, callbacks);
129+
onStoreGeneratorRegistered(storeName: string, callback: ItemRegistrationCallback<StoreGenerator>): void {
130+
storeGeneratorRegistry.onItemRegistered(storeName, callback);
175131
},
176132

177133
/**
178134
* Used by components to get the store generator, waiting for it to be registered if necessary.
179135
* @param name Name of the store generator to wait for
180136
* @returns Promise that resolves with the StoreGenerator once registered
181137
*/
182-
async getOrWaitForStoreGenerator(name: string): Promise<StoreGenerator> {
183-
return new Promise((resolve) => {
184-
this.onStoreGeneratorRegistered(name, resolve);
185-
});
138+
getOrWaitForStoreGenerator(name: string): Promise<StoreGenerator> {
139+
return storeGeneratorRegistry.getOrWaitForItem(name);
186140
},
187141
};

node_package/src/types/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ export interface RegisteredComponent {
107107
isRenderer: boolean;
108108
}
109109

110-
export type ComponentRegistrationCallback = (component: RegisteredComponent) => void;
110+
export type ItemRegistrationCallback<T> = (component: T) => void;
111111

112112
interface Params {
113113
props?: Record<string, unknown>;

0 commit comments

Comments
 (0)