hook.tsx 1.8 KB

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