hook.tsx 1.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980
  1. import {Component} from 'react';
  2. import HookStore from 'sentry/stores/hookStore';
  3. import {HookName, Hooks} from 'sentry/types/hooks';
  4. type Props<H extends HookName> = {
  5. /**
  6. * The name of the hook as listed in hookstore.add(hookName, callback)
  7. */
  8. name: H;
  9. /**
  10. * If children are provided as a function to the Hook, the hooks will be
  11. * passed down as a render prop.
  12. */
  13. children?: (opts: {hooks: Array<Hooks[H]>}) => React.ReactNode;
  14. } & Omit<Parameters<Hooks[H]>[0], 'name'>;
  15. type HookState<H extends HookName> = {
  16. hooks: Array<Hooks[H]>;
  17. };
  18. /**
  19. * Instead of accessing the HookStore directly, use this.
  20. *
  21. * If the hook slot needs to perform anything w/ the hooks, you can pass a
  22. * function as a child and you will receive an object with a `hooks` key
  23. *
  24. * Example:
  25. *
  26. * <Hook name="my-hook">
  27. * {({hooks}) => hooks.map(hook => (
  28. * <Wrapper>{hook}</Wrapper>
  29. * ))}
  30. * </Hook>
  31. */
  32. function Hook<H extends HookName>({name, ...props}: Props<H>) {
  33. class HookComponent extends Component<{}, HookState<H>> {
  34. static displayName = `Hook(${name})`;
  35. state = {
  36. hooks: HookStore.get(name).map(cb => cb(props)),
  37. };
  38. componentWillUnmount() {
  39. this.unsubscribe();
  40. }
  41. handleHooks(hookName: HookName, hooks: Array<Hooks[H]>) {
  42. // Make sure that the incoming hook update matches this component's hook name
  43. if (hookName !== name) {
  44. return;
  45. }
  46. this.setState({hooks: hooks.map(cb => cb(props))});
  47. }
  48. unsubscribe = HookStore.listen(
  49. (hookName: HookName, hooks: Array<Hooks[H]>) => this.handleHooks(hookName, hooks),
  50. undefined
  51. );
  52. render() {
  53. const {children} = props;
  54. if (!this.state.hooks || !this.state.hooks.length) {
  55. return null;
  56. }
  57. if (typeof children === 'function') {
  58. return children({hooks: this.state.hooks});
  59. }
  60. return this.state.hooks;
  61. }
  62. }
  63. return <HookComponent />;
  64. }
  65. export default Hook;