withPlugins.tsx 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. import * as React from 'react';
  2. import createReactClass from 'create-react-class';
  3. import Reflux from 'reflux';
  4. import {fetchPlugins} from 'app/actionCreators/plugins';
  5. import PluginsStore from 'app/stores/pluginsStore';
  6. import {Organization, Plugin, Project} from 'app/types';
  7. import {defined} from 'app/utils';
  8. import getDisplayName from 'app/utils/getDisplayName';
  9. import withOrganization from 'app/utils/withOrganization';
  10. import withProject from 'app/utils/withProject';
  11. type WithPluginProps = {
  12. organization: Organization;
  13. project?: Project;
  14. };
  15. type InjectedPluginProps = {
  16. plugins: {plugins: Plugin[]; loading: boolean};
  17. };
  18. /**
  19. * Higher order component that fetches list of plugins and
  20. * passes PluginsStore to component as `plugins`
  21. */
  22. const withPlugins = <P extends WithPluginProps>(
  23. WrappedComponent: React.ComponentType<P>
  24. ) =>
  25. withOrganization(
  26. withProject(
  27. createReactClass<Omit<P, keyof InjectedPluginProps> & WithPluginProps, {}>({
  28. displayName: `withPlugins(${getDisplayName(WrappedComponent)})`,
  29. mixins: [Reflux.connect(PluginsStore, 'store') as any],
  30. componentDidMount() {
  31. this.fetchPlugins();
  32. },
  33. componentDidUpdate(prevProps, _prevState, prevContext) {
  34. const {organization, project} = this.props;
  35. // Only fetch plugins when a org slug or project slug has changed
  36. const prevOrg =
  37. prevProps.organization || (prevContext && prevContext.organization);
  38. const prevProject = prevProps.project || (prevContext && prevContext.project);
  39. // If previous org/project is undefined then it means:
  40. // the HoC has mounted, `fetchPlugins` has been called (via cDM), and
  41. // store was updated. We don't need to fetchPlugins again (or it will cause an infinite loop)
  42. //
  43. // This is for the unusual case where component is mounted and receives a new org/project prop
  44. // e.g. when switching projects via breadcrumbs in settings.
  45. if (!defined(prevProject) || !defined(prevOrg)) {
  46. return;
  47. }
  48. const isOrgSame = prevOrg.slug === organization.slug;
  49. const isProjectSame = prevProject.slug === project?.slug;
  50. // Don't do anything if org and project are the same
  51. if (isOrgSame && isProjectSame) {
  52. return;
  53. }
  54. this.fetchPlugins();
  55. },
  56. fetchPlugins() {
  57. const {organization, project} = this.props;
  58. if (!project || !organization) {
  59. return;
  60. }
  61. fetchPlugins({projectId: project.slug, orgId: organization.slug});
  62. },
  63. render() {
  64. return (
  65. <WrappedComponent
  66. {...(this.props as P & WithPluginProps)}
  67. plugins={this.state.store}
  68. />
  69. );
  70. },
  71. })
  72. )
  73. );
  74. export default withPlugins;