transitionChart.tsx 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. import {Component, Fragment} from 'react';
  2. import LoadingPanel from 'sentry/components/charts/loadingPanel';
  3. const defaultProps = {
  4. height: '200px',
  5. };
  6. type Props = {
  7. loading: boolean;
  8. reloading: boolean;
  9. } & typeof defaultProps;
  10. type State = {
  11. key: number;
  12. prevLoading: boolean;
  13. prevReloading: boolean;
  14. };
  15. class TransitionChart extends Component<Props, State> {
  16. static defaultProps = defaultProps;
  17. state: State = {
  18. prevReloading: this.props.reloading,
  19. prevLoading: this.props.loading,
  20. key: 1,
  21. };
  22. static getDerivedStateFromProps(props: Readonly<Props>, state: State) {
  23. // Transitions are controlled using variables called:
  24. // - loading and,
  25. // - reloading (also called pending in other apps)
  26. //
  27. // This component remounts the chart to ensure the stable transition
  28. // from one dataset to the next.
  29. const prevReloading = state.prevReloading;
  30. const nextReloading = props.reloading;
  31. const prevLoading = state.prevLoading;
  32. const nextLoading = props.loading;
  33. // whenever loading changes, we explicitly remount the children by updating
  34. // the key prop; regardless of what state reloading is in
  35. if (prevLoading !== nextLoading) {
  36. return {
  37. prevReloading: nextReloading,
  38. prevLoading: nextLoading,
  39. key: state.key + 1,
  40. };
  41. }
  42. // invariant: prevLoading === nextLoading
  43. // if loading is true, and hasn't changed from the previous re-render,
  44. // do not remount the children.
  45. if (nextLoading) {
  46. return {
  47. prevReloading: nextReloading,
  48. prevLoading: nextLoading,
  49. key: state.key,
  50. };
  51. }
  52. // invariant: loading is false
  53. // whenever the chart is transitioning from the reloading (pending) state to a non-loading state,
  54. // remount the children
  55. if (prevReloading && !nextReloading) {
  56. return {
  57. prevReloading: nextReloading,
  58. prevLoading: nextLoading,
  59. key: state.key + 1,
  60. };
  61. }
  62. // do not remount the children in these remaining cases:
  63. // !prevReloading && !nextReloading (re-render with no prop change)
  64. // prevReloading && nextReloading (re-render with no prop change)
  65. // !prevReloading && nextReloading (from loaded to pending state)
  66. return {
  67. prevReloading: nextReloading,
  68. prevLoading: nextLoading,
  69. key: state.key,
  70. };
  71. }
  72. render() {
  73. const {height, loading, reloading} = this.props;
  74. if (loading && !reloading) {
  75. return <LoadingPanel height={height} data-test-id="events-request-loading" />;
  76. }
  77. // We make use of the key prop to explicitly remount the children
  78. // https://reactjs.org/docs/lists-and-keys.html#keys
  79. return <Fragment key={String(this.state.key)}>{this.props.children}</Fragment>;
  80. }
  81. }
  82. export default TransitionChart;