transitionChart.tsx 2.8 KB

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