hookStore.tsx 2.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283
  1. import type {StoreDefinition} from 'reflux';
  2. import {createStore} from 'reflux';
  3. import type {HookName, Hooks} from 'sentry/types/hooks';
  4. interface Internals {
  5. // XXX(epurkhiser): We could type this as {[H in HookName]?:
  6. // Array<Hooks[H]>}, however this causes typescript to produce a complex
  7. // union that it complains is 'too complex'
  8. hooks: any;
  9. }
  10. // TODO: Make generic and match against a map of allowed callbacks if we expand this pattern.
  11. type HookCallback = (...args: any[]) => void;
  12. interface HookStoreDefinition extends StoreDefinition, Internals {
  13. add<H extends HookName>(hookName: H, callback: Hooks[H]): void;
  14. get<H extends HookName>(hookName: H): Array<Hooks[H]>;
  15. getCallback<H extends HookName>(hookName: H, key: string): HookCallback | undefined;
  16. init(): void;
  17. persistCallback<H extends HookName>(
  18. hookName: H,
  19. key: string,
  20. value: HookCallback
  21. ): void;
  22. remove<H extends HookName>(hookName: H, callback: Hooks[H]): void;
  23. }
  24. const storeConfig: HookStoreDefinition = {
  25. hooks: {},
  26. hookCallbacks: {},
  27. init() {
  28. // XXX: Do not use `this.listenTo` in this store. We avoid usage of reflux
  29. // listeners due to their leaky nature in tests.
  30. this.hooks = {};
  31. this.hookCallbacks = {}; // For persisting hook pure functions / useX react hooks remotely.
  32. },
  33. add(hookName, callback) {
  34. if (this.hooks[hookName] === undefined) {
  35. this.hooks[hookName] = [];
  36. }
  37. this.hooks[hookName].push(callback);
  38. this.trigger(hookName, this.hooks[hookName]);
  39. },
  40. remove(hookName, callback) {
  41. if (this.hooks[hookName] === undefined) {
  42. return;
  43. }
  44. this.hooks[hookName] = this.hooks[hookName]!.filter(cb => cb !== callback);
  45. this.trigger(hookName, this.hooks[hookName]);
  46. },
  47. get(hookName) {
  48. return this.hooks[hookName]! || [];
  49. },
  50. persistCallback(hookName, key, value) {
  51. if (this.hookCallbacks[hookName] === undefined) {
  52. this.hookCallbacks[hookName] = {};
  53. }
  54. if (this.hookCallbacks[hookName][key] !== value) {
  55. this.hookCallbacks[hookName][key] = value;
  56. }
  57. },
  58. getCallback(hookName, key) {
  59. return this.hookCallbacks[hookName]?.[key];
  60. },
  61. };
  62. /**
  63. * HookStore is used to allow extensibility into Sentry's frontend via
  64. * registration of 'hook functions'.
  65. *
  66. * This functionality is primarily used by the SASS sentry.io product.
  67. */
  68. const HookStore = createStore(storeConfig);
  69. export default HookStore;