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 type {ReactContext} from 'shared/ReactTypes';
    
  11. import * as React from 'react';
    
  12. import {
    
  13.   createContext,
    
  14.   Component,
    
  15.   forwardRef,
    
  16.   Fragment,
    
  17.   memo,
    
  18.   useCallback,
    
  19.   useDebugValue,
    
  20.   useEffect,
    
  21.   useReducer,
    
  22.   useState,
    
  23. } from 'react';
    
  24. 
    
  25. const initialData = {foo: 'FOO', bar: 'BAR'};
    
  26. 
    
  27. // $FlowFixMe[missing-local-annot]
    
  28. function reducer(state, action: {type: string}) {
    
  29.   switch (action.type) {
    
  30.     case 'swap':
    
  31.       return {foo: state.bar, bar: state.foo};
    
  32.     default:
    
  33.       throw new Error();
    
  34.   }
    
  35. }
    
  36. 
    
  37. type StatefulFunctionProps = {name: string};
    
  38. 
    
  39. function StatefulFunction({name}: StatefulFunctionProps) {
    
  40.   const [count, updateCount] = useState(0);
    
  41.   const debouncedCount = useDebounce(count, 1000);
    
  42.   const handleUpdateCountClick = useCallback(
    
  43.     () => updateCount(count + 1),
    
  44.     [count],
    
  45.   );
    
  46. 
    
  47.   const [data, dispatch] = useReducer(reducer, initialData);
    
  48.   const handleUpdateReducerClick = useCallback(
    
  49.     () => dispatch({type: 'swap'}),
    
  50.     [],
    
  51.   );
    
  52. 
    
  53.   return (
    
  54.     <ul>
    
  55.       <li>Name: {name}</li>
    
  56.       <li>
    
  57.         <button onClick={handleUpdateCountClick}>
    
  58.           Debounced count: {debouncedCount}
    
  59.         </button>
    
  60.       </li>
    
  61.       <li>
    
  62.         Reducer state: foo "{data.foo}", bar "{data.bar}"
    
  63.       </li>
    
  64.       <li>
    
  65.         <button onClick={handleUpdateReducerClick}>Swap reducer values</button>
    
  66.       </li>
    
  67.     </ul>
    
  68.   );
    
  69. }
    
  70. 
    
  71. const BoolContext = createContext(true);
    
  72. BoolContext.displayName = 'BoolContext';
    
  73. 
    
  74. type Props = {name: string, toggle: boolean};
    
  75. type State = {cities: Array<string>, state: string};
    
  76. 
    
  77. class StatefulClass extends Component<Props, State> {
    
  78.   static contextType: ReactContext<boolean> = BoolContext;
    
  79. 
    
  80.   state: State = {
    
  81.     cities: ['San Francisco', 'San Jose'],
    
  82.     state: 'California',
    
  83.   };
    
  84. 
    
  85.   // $FlowFixMe[missing-local-annot]
    
  86.   handleChange = ({target}): any =>
    
  87.     this.setState({
    
  88.       state: target.value,
    
  89.     });
    
  90. 
    
  91.   render(): any {
    
  92.     return (
    
  93.       <ul>
    
  94.         <li>Name: {this.props.name}</li>
    
  95.         <li>Toggle: {this.props.toggle ? 'true' : 'false'}</li>
    
  96.         <li>
    
  97.           State: <input value={this.state.state} onChange={this.handleChange} />
    
  98.         </li>
    
  99.         <li>Cities: {this.state.cities.join(', ')}</li>
    
  100.         <li>Context: {this.context ? 'true' : 'false'}</li>
    
  101.       </ul>
    
  102.     );
    
  103.   }
    
  104. }
    
  105. 
    
  106. const MemoizedStatefulClass = memo(StatefulClass);
    
  107. const MemoizedStatefulFunction = memo(StatefulFunction);
    
  108. 
    
  109. const ForwardRef = forwardRef<{name: string}, HTMLUListElement>(
    
  110.   ({name}, ref) => {
    
  111.     const [count, updateCount] = useState(0);
    
  112.     const debouncedCount = useDebounce(count, 1000);
    
  113.     const handleUpdateCountClick = useCallback(
    
  114.       () => updateCount(count + 1),
    
  115.       [count],
    
  116.     );
    
  117.     return (
    
  118.       <ul ref={ref}>
    
  119.         <li>Name: {name}</li>
    
  120.         <li>
    
  121.           <button onClick={handleUpdateCountClick}>
    
  122.             Debounced count: {debouncedCount}
    
  123.           </button>
    
  124.         </li>
    
  125.       </ul>
    
  126.     );
    
  127.   },
    
  128. );
    
  129. 
    
  130. export default function EditableProps(): React.Node {
    
  131.   return (
    
  132.     <Fragment>
    
  133.       <h1>Editable props</h1>
    
  134.       <strong>Class</strong>
    
  135.       <StatefulClass name="Brian" toggle={true} />
    
  136.       <strong>Function</strong>
    
  137.       <StatefulFunction name="Brian" />
    
  138.       <strong>Memoized Class</strong>
    
  139.       <MemoizedStatefulClass name="Brian" toggle={true} />
    
  140.       <strong>Memoized Function</strong>
    
  141.       <MemoizedStatefulFunction name="Brian" />
    
  142.       <strong>Forward Ref</strong>
    
  143.       <ForwardRef name="Brian" />
    
  144.     </Fragment>
    
  145.   );
    
  146. }
    
  147. 
    
  148. // Below copied from https://usehooks.com/
    
  149. function useDebounce(value: number, delay: number) {
    
  150.   // State and setters for debounced value
    
  151.   const [debouncedValue, setDebouncedValue] = useState(value);
    
  152. 
    
  153.   // Show the value in DevTools
    
  154.   useDebugValue(debouncedValue);
    
  155. 
    
  156.   useEffect(
    
  157.     () => {
    
  158.       // Update debounced value after delay
    
  159.       const handler = setTimeout(() => {
    
  160.         setDebouncedValue(value);
    
  161.       }, delay);
    
  162. 
    
  163.       // Cancel the timeout if value changes (also on delay change or unmount)
    
  164.       // This is how we prevent debounced value from updating if value is changed ...
    
  165.       // .. within the delay period. Timeout gets cleared and restarted.
    
  166.       return () => {
    
  167.         clearTimeout(handler);
    
  168.       };
    
  169.     },
    
  170.     [value, delay], // Only re-call effect if value or delay changes
    
  171.   );
    
  172. 
    
  173.   return debouncedValue;
    
  174. }
    
  175. // Above copied from https://usehooks.com/