1. /* eslint-disable react/react-in-jsx-scope, react/jsx-no-undef */
    
  2. /* global React ReactCache ReactDOM SchedulerTracing ScheduleTracing  */
    
  3. 
    
  4. const apps = [];
    
  5. 
    
  6. const pieces = React.version.split('.');
    
  7. const major =
    
  8.   pieces[0] === '0' ? parseInt(pieces[1], 10) : parseInt(pieces[0], 10);
    
  9. const minor =
    
  10.   pieces[0] === '0' ? parseInt(pieces[2], 10) : parseInt(pieces[1], 10);
    
  11. 
    
  12. // Convenience wrapper to organize API features in DevTools.
    
  13. function Feature({children, label, version}) {
    
  14.   return (
    
  15.     <div className="Feature">
    
  16.       <div className="FeatureHeader">
    
  17.         <code className="FeatureCode">{label}</code>
    
  18.         <small>{version}</small>
    
  19.       </div>
    
  20.       {children}
    
  21.     </div>
    
  22.   );
    
  23. }
    
  24. 
    
  25. // Simplify interaction tracing for tests below.
    
  26. let trace = null;
    
  27. if (typeof SchedulerTracing !== 'undefined') {
    
  28.   trace = SchedulerTracing.unstable_trace;
    
  29. } else if (typeof ScheduleTracing !== 'undefined') {
    
  30.   trace = ScheduleTracing.unstable_trace;
    
  31. } else {
    
  32.   trace = (_, __, callback) => callback();
    
  33. }
    
  34. 
    
  35. // https://github.com/facebook/react/blob/main/CHANGELOG.md
    
  36. switch (major) {
    
  37.   case 16:
    
  38.     switch (minor) {
    
  39.       case 7:
    
  40.         if (typeof React.useState === 'function') {
    
  41.           // Hooks
    
  42.           function Hooks() {
    
  43.             const [count, setCount] = React.useState(0);
    
  44.             const incrementCount = React.useCallback(
    
  45.               () => setCount(count + 1),
    
  46.               [count]
    
  47.             );
    
  48.             return (
    
  49.               <div>
    
  50.                 count: {count}{' '}
    
  51.                 <button onClick={incrementCount}>increment</button>
    
  52.               </div>
    
  53.             );
    
  54.           }
    
  55.           apps.push(
    
  56.             <Feature key="Hooks" label="Hooks" version="16.7+">
    
  57.               <Hooks />
    
  58.             </Feature>
    
  59.           );
    
  60.         }
    
  61.       case 6:
    
  62.         // memo
    
  63.         function LabelComponent({label}) {
    
  64.           return <label>{label}</label>;
    
  65.         }
    
  66.         const AnonymousMemoized = React.memo(({label}) => (
    
  67.           <label>{label}</label>
    
  68.         ));
    
  69.         const Memoized = React.memo(LabelComponent);
    
  70.         const CustomMemoized = React.memo(LabelComponent);
    
  71.         CustomMemoized.displayName = 'MemoizedLabelFunction';
    
  72.         apps.push(
    
  73.           <Feature key="memo" label="memo" version="16.6+">
    
  74.             <AnonymousMemoized label="AnonymousMemoized" />
    
  75.             <Memoized label="Memoized" />
    
  76.             <CustomMemoized label="CustomMemoized" />
    
  77.           </Feature>
    
  78.         );
    
  79. 
    
  80.         // Suspense
    
  81.         const loadResource = ([text, ms]) => {
    
  82.           return new Promise((resolve, reject) => {
    
  83.             setTimeout(() => {
    
  84.               resolve(text);
    
  85.             }, ms);
    
  86.           });
    
  87.         };
    
  88.         const getResourceKey = ([text, ms]) => text;
    
  89.         const Resource = ReactCache.unstable_createResource(
    
  90.           loadResource,
    
  91.           getResourceKey
    
  92.         );
    
  93.         class Suspending extends React.Component {
    
  94.           state = {useSuspense: false};
    
  95.           useSuspense = () => this.setState({useSuspense: true});
    
  96.           render() {
    
  97.             if (this.state.useSuspense) {
    
  98.               const text = Resource.read(['loaded', 2000]);
    
  99.               return text;
    
  100.             } else {
    
  101.               return <button onClick={this.useSuspense}>load data</button>;
    
  102.             }
    
  103.           }
    
  104.         }
    
  105.         apps.push(
    
  106.           <Feature key="Suspense" label="Suspense" version="16.6+">
    
  107.             <React.Suspense fallback={<div>loading...</div>}>
    
  108.               <Suspending />
    
  109.             </React.Suspense>
    
  110.           </Feature>
    
  111.         );
    
  112. 
    
  113.         // lazy
    
  114.         const LazyWithDefaultProps = React.lazy(
    
  115.           () =>
    
  116.             new Promise(resolve => {
    
  117.               function FooWithDefaultProps(props) {
    
  118.                 return (
    
  119.                   <h1>
    
  120.                     {props.greeting}, {props.name}
    
  121.                   </h1>
    
  122.                 );
    
  123.               }
    
  124.               FooWithDefaultProps.defaultProps = {
    
  125.                 name: 'World',
    
  126.                 greeting: 'Bonjour',
    
  127.               };
    
  128.               resolve({
    
  129.                 default: FooWithDefaultProps,
    
  130.               });
    
  131.             })
    
  132.         );
    
  133.         apps.push(
    
  134.           <Feature key="lazy" label="lazy" version="16.6+">
    
  135.             <React.Suspense fallback={<div>loading...</div>}>
    
  136.               <LazyWithDefaultProps greeting="Hello" />
    
  137.             </React.Suspense>
    
  138.           </Feature>
    
  139.         );
    
  140.       case 5:
    
  141.       case 4:
    
  142.         // unstable_Profiler
    
  143.         class ProfilerChild extends React.Component {
    
  144.           state = {count: 0};
    
  145.           incrementCount = () =>
    
  146.             this.setState(prevState => ({count: prevState.count + 1}));
    
  147.           render() {
    
  148.             return (
    
  149.               <div>
    
  150.                 count: {this.state.count}{' '}
    
  151.                 <button onClick={this.incrementCount}>increment</button>
    
  152.               </div>
    
  153.             );
    
  154.           }
    
  155.         }
    
  156.         const onRender = (...args) => {};
    
  157.         const Profiler = React.unstable_Profiler || React.Profiler;
    
  158.         apps.push(
    
  159.           <Feature
    
  160.             key="unstable_Profiler"
    
  161.             label="unstable_Profiler"
    
  162.             version="16.4+">
    
  163.             <Profiler id="count" onRender={onRender}>
    
  164.               <div>
    
  165.                 <ProfilerChild />
    
  166.               </div>
    
  167.             </Profiler>
    
  168.           </Feature>
    
  169.         );
    
  170.       case 3:
    
  171.         // createContext()
    
  172.         const LocaleContext = React.createContext();
    
  173.         LocaleContext.displayName = 'LocaleContext';
    
  174.         const ThemeContext = React.createContext();
    
  175.         apps.push(
    
  176.           <Feature key="createContext" label="createContext" version="16.3+">
    
  177.             <ThemeContext.Provider value="blue">
    
  178.               <ThemeContext.Consumer>
    
  179.                 {theme => <div>theme: {theme}</div>}
    
  180.               </ThemeContext.Consumer>
    
  181.             </ThemeContext.Provider>
    
  182.             <LocaleContext.Provider value="en-US">
    
  183.               <LocaleContext.Consumer>
    
  184.                 {locale => <div>locale: {locale}</div>}
    
  185.               </LocaleContext.Consumer>
    
  186.             </LocaleContext.Provider>
    
  187.           </Feature>
    
  188.         );
    
  189. 
    
  190.         // forwardRef()
    
  191.         const AnonymousFunction = React.forwardRef((props, ref) => (
    
  192.           <div ref={ref}>{props.children}</div>
    
  193.         ));
    
  194.         const NamedFunction = React.forwardRef(function named(props, ref) {
    
  195.           return <div ref={ref}>{props.children}</div>;
    
  196.         });
    
  197.         const CustomName = React.forwardRef((props, ref) => (
    
  198.           <div ref={ref}>{props.children}</div>
    
  199.         ));
    
  200.         CustomName.displayName = 'CustomNameForwardRef';
    
  201.         apps.push(
    
  202.           <Feature key="forwardRef" label="forwardRef" version="16.3+">
    
  203.             <AnonymousFunction>AnonymousFunction</AnonymousFunction>
    
  204.             <NamedFunction>NamedFunction</NamedFunction>
    
  205.             <CustomName>CustomName</CustomName>
    
  206.           </Feature>
    
  207.         );
    
  208. 
    
  209.         // StrictMode
    
  210.         class StrictModeChild extends React.Component {
    
  211.           render() {
    
  212.             return 'StrictModeChild';
    
  213.           }
    
  214.         }
    
  215.         apps.push(
    
  216.           <Feature key="StrictMode" label="StrictMode" version="16.3+">
    
  217.             <React.StrictMode>
    
  218.               <StrictModeChild />
    
  219.             </React.StrictMode>
    
  220.           </Feature>
    
  221.         );
    
  222. 
    
  223.         // unstable_AsyncMode (later renamed to unstable_ConcurrentMode, then ConcurrentMode)
    
  224.         const ConcurrentMode =
    
  225.           React.ConcurrentMode ||
    
  226.           React.unstable_ConcurrentMode ||
    
  227.           React.unstable_AsyncMode;
    
  228.         apps.push(
    
  229.           <Feature
    
  230.             key="AsyncMode/ConcurrentMode"
    
  231.             label="AsyncMode/ConcurrentMode"
    
  232.             version="16.3+">
    
  233.             <ConcurrentMode>
    
  234.               <div>
    
  235.                 unstable_AsyncMode was added in 16.3, renamed to
    
  236.                 unstable_ConcurrentMode in 16.5, and then renamed to
    
  237.                 ConcurrentMode in 16.7
    
  238.               </div>
    
  239.             </ConcurrentMode>
    
  240.           </Feature>
    
  241.         );
    
  242.       case 2:
    
  243.         // Fragment
    
  244.         apps.push(
    
  245.           <Feature key="Fragment" label="Fragment" version="16.4+">
    
  246.             <React.Fragment>
    
  247.               <div>one</div>
    
  248.               <div>two</div>
    
  249.             </React.Fragment>
    
  250.           </Feature>
    
  251.         );
    
  252.       case 1:
    
  253.       case 0:
    
  254.       default:
    
  255.         break;
    
  256.     }
    
  257.     break;
    
  258.   case 15:
    
  259.     break;
    
  260.   case 14:
    
  261.     break;
    
  262.   default:
    
  263.     break;
    
  264. }
    
  265. 
    
  266. function Even() {
    
  267.   return <small>(even)</small>;
    
  268. }
    
  269. 
    
  270. // Simple stateful app shared by all React versions
    
  271. class SimpleApp extends React.Component {
    
  272.   state = {count: 0};
    
  273.   incrementCount = () => {
    
  274.     const updaterFn = prevState => ({count: prevState.count + 1});
    
  275.     trace('Updating count', performance.now(), () => this.setState(updaterFn));
    
  276.   };
    
  277.   render() {
    
  278.     const {count} = this.state;
    
  279.     return (
    
  280.       <div>
    
  281.         {count % 2 === 0 ? (
    
  282.           <span>
    
  283.             count: {count} <Even />
    
  284.           </span>
    
  285.         ) : (
    
  286.           <span>count: {count}</span>
    
  287.         )}{' '}
    
  288.         <button onClick={this.incrementCount}>increment</button>
    
  289.       </div>
    
  290.     );
    
  291.   }
    
  292. }
    
  293. apps.push(
    
  294.   <Feature key="Simple stateful app" label="Simple stateful app" version="any">
    
  295.     <SimpleApp />
    
  296.   </Feature>
    
  297. );
    
  298. 
    
  299. // This component, with the version prop, helps organize DevTools at a glance.
    
  300. function TopLevelWrapperForDevTools({version}) {
    
  301.   let header = <h1>React {version}</h1>;
    
  302.   if (version.includes('canary')) {
    
  303.     const commitSha = version.match(/.+canary-(.+)/)[1];
    
  304.     header = (
    
  305.       <h1>
    
  306.         React canary{' '}
    
  307.         <a href={`https://github.com/facebook/react/commit/${commitSha}`}>
    
  308.           {commitSha}
    
  309.         </a>
    
  310.       </h1>
    
  311.     );
    
  312.   } else if (version.includes('alpha')) {
    
  313.     header = <h1>React next</h1>;
    
  314.   }
    
  315. 
    
  316.   return (
    
  317.     <div>
    
  318.       {header}
    
  319.       {apps}
    
  320.     </div>
    
  321.   );
    
  322. }
    
  323. TopLevelWrapperForDevTools.displayName = 'React';
    
  324. 
    
  325. ReactDOM.render(
    
  326.   <TopLevelWrapperForDevTools version={React.version} />,
    
  327.   document.getElementById('root')
    
  328. );