1. /**
    
  2.  * Copyright (c) Meta Platforms, Inc. and affiliates.
    
  3.  *
    
  4.  * This source code is licensed under the MIT license found in the
    
  5.  * LICENSE file in the root directory of this source tree.
    
  6.  *
    
  7.  * @flow
    
  8.  */
    
  9. 
    
  10. import * as React from 'react';
    
  11. import {useContext} from 'react';
    
  12. import {ProfilerContext} from '../Profiler/ProfilerContext';
    
  13. import {StoreContext} from '../context';
    
  14. 
    
  15. import styles from './WhatChanged.css';
    
  16. 
    
  17. function hookIndicesToString(indices: Array<number>): string {
    
  18.   // This is debatable but I think 1-based might ake for a nicer UX.
    
  19.   const numbers = indices.map(value => value + 1);
    
  20. 
    
  21.   switch (numbers.length) {
    
  22.     case 0:
    
  23.       return 'No hooks changed';
    
  24.     case 1:
    
  25.       return `Hook ${numbers[0]} changed`;
    
  26.     case 2:
    
  27.       return `Hooks ${numbers[0]} and ${numbers[1]} changed`;
    
  28.     default:
    
  29.       return `Hooks ${numbers.slice(0, numbers.length - 1).join(', ')} and ${
    
  30.         numbers[numbers.length - 1]
    
  31.       } changed`;
    
  32.   }
    
  33. }
    
  34. 
    
  35. type Props = {
    
  36.   fiberID: number,
    
  37. };
    
  38. 
    
  39. export default function WhatChanged({fiberID}: Props): React.Node {
    
  40.   const {profilerStore} = useContext(StoreContext);
    
  41.   const {rootID, selectedCommitIndex} = useContext(ProfilerContext);
    
  42. 
    
  43.   // TRICKY
    
  44.   // Handle edge case where no commit is selected because of a min-duration filter update.
    
  45.   // If the commit index is null, suspending for data below would throw an error.
    
  46.   // TODO (ProfilerContext) This check should not be necessary.
    
  47.   if (selectedCommitIndex === null) {
    
  48.     return null;
    
  49.   }
    
  50. 
    
  51.   const {changeDescriptions} = profilerStore.getCommitData(
    
  52.     ((rootID: any): number),
    
  53.     selectedCommitIndex,
    
  54.   );
    
  55. 
    
  56.   if (changeDescriptions === null) {
    
  57.     return null;
    
  58.   }
    
  59. 
    
  60.   const changeDescription = changeDescriptions.get(fiberID);
    
  61.   if (changeDescription == null) {
    
  62.     return null;
    
  63.   }
    
  64. 
    
  65.   const {context, didHooksChange, hooks, isFirstMount, props, state} =
    
  66.     changeDescription;
    
  67. 
    
  68.   if (isFirstMount) {
    
  69.     return (
    
  70.       <div className={styles.Component}>
    
  71.         <label className={styles.Label}>Why did this render?</label>
    
  72.         <div className={styles.Item}>
    
  73.           This is the first time the component rendered.
    
  74.         </div>
    
  75.       </div>
    
  76.     );
    
  77.   }
    
  78. 
    
  79.   const changes = [];
    
  80. 
    
  81.   if (context === true) {
    
  82.     changes.push(
    
  83.       <div key="context" className={styles.Item}>
    
  84.         • Context changed
    
  85.       </div>,
    
  86.     );
    
  87.   } else if (
    
  88.     typeof context === 'object' &&
    
  89.     context !== null &&
    
  90.     context.length !== 0
    
  91.   ) {
    
  92.     changes.push(
    
  93.       <div key="context" className={styles.Item}>
    
  94.         • Context changed:
    
  95.         {context.map(key => (
    
  96.           <span key={key} className={styles.Key}>
    
  97.             {key}
    
  98.           </span>
    
  99.         ))}
    
  100.       </div>,
    
  101.     );
    
  102.   }
    
  103. 
    
  104.   if (didHooksChange) {
    
  105.     if (Array.isArray(hooks)) {
    
  106.       changes.push(
    
  107.         <div key="hooks" className={styles.Item}>
    
  108.           • {hookIndicesToString(hooks)}
    
  109.         </div>,
    
  110.       );
    
  111.     } else {
    
  112.       changes.push(
    
  113.         <div key="hooks" className={styles.Item}>
    
  114.           • Hooks changed
    
  115.         </div>,
    
  116.       );
    
  117.     }
    
  118.   }
    
  119. 
    
  120.   if (props !== null && props.length !== 0) {
    
  121.     changes.push(
    
  122.       <div key="props" className={styles.Item}>
    
  123.         • Props changed:
    
  124.         {props.map(key => (
    
  125.           <span key={key} className={styles.Key}>
    
  126.             {key}
    
  127.           </span>
    
  128.         ))}
    
  129.       </div>,
    
  130.     );
    
  131.   }
    
  132. 
    
  133.   if (state !== null && state.length !== 0) {
    
  134.     changes.push(
    
  135.       <div key="state" className={styles.Item}>
    
  136.         • State changed:
    
  137.         {state.map(key => (
    
  138.           <span key={key} className={styles.Key}>
    
  139.             {key}
    
  140.           </span>
    
  141.         ))}
    
  142.       </div>,
    
  143.     );
    
  144.   }
    
  145. 
    
  146.   if (changes.length === 0) {
    
  147.     changes.push(
    
  148.       <div key="nothing" className={styles.Item}>
    
  149.         The parent component rendered.
    
  150.       </div>,
    
  151.     );
    
  152.   }
    
  153. 
    
  154.   return (
    
  155.     <div className={styles.Component}>
    
  156.       <label className={styles.Label}>Why did this render?</label>
    
  157.       {changes}
    
  158.     </div>
    
  159.   );
    
  160. }