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 {
    
  12.   createContext,
    
  13.   forwardRef,
    
  14.   Fragment,
    
  15.   memo,
    
  16.   useCallback,
    
  17.   useContext,
    
  18.   useDebugValue,
    
  19.   useEffect,
    
  20.   useState,
    
  21. } from 'react';
    
  22. 
    
  23. const object = {
    
  24.   string: 'abc',
    
  25.   number: 123,
    
  26.   boolean: true,
    
  27.   null: null,
    
  28.   undefined: undefined,
    
  29.   array: ['a', 'b', 'c'],
    
  30.   object: {foo: 1, bar: 2, baz: 3},
    
  31. };
    
  32. 
    
  33. function useNestedInnerHook() {
    
  34.   return useState(123);
    
  35. }
    
  36. function useNestedOuterHook() {
    
  37.   return useNestedInnerHook();
    
  38. }
    
  39. 
    
  40. function useCustomObject() {
    
  41.   useDebugValue(object);
    
  42.   return useState(123);
    
  43. }
    
  44. 
    
  45. function useDeepHookA() {
    
  46.   useDebugValue('useDeepHookA');
    
  47.   useDeepHookB();
    
  48. }
    
  49. function useDeepHookB() {
    
  50.   useDebugValue('useDeepHookB');
    
  51.   useDeepHookC();
    
  52. }
    
  53. function useDeepHookC() {
    
  54.   useDebugValue('useDeepHookC');
    
  55.   useDeepHookD();
    
  56. }
    
  57. function useDeepHookD() {
    
  58.   useDebugValue('useDeepHookD');
    
  59.   useDeepHookE();
    
  60. }
    
  61. function useDeepHookE() {
    
  62.   useDebugValue('useDeepHookE');
    
  63.   useDeepHookF();
    
  64. }
    
  65. function useDeepHookF() {
    
  66.   useDebugValue('useDeepHookF');
    
  67. }
    
  68. 
    
  69. const ContextA = createContext('A');
    
  70. const ContextB = createContext('B');
    
  71. 
    
  72. function FunctionWithHooks(props: any, ref: React$Ref<any>) {
    
  73.   const [count, updateCount] = useState(0);
    
  74.   // eslint-disable-next-line no-unused-vars
    
  75.   const contextValueA = useContext(ContextA);
    
  76. 
    
  77.   // eslint-disable-next-line no-unused-vars
    
  78.   const [_, __] = useState(object);
    
  79. 
    
  80.   // Custom hook with a custom debug label
    
  81.   const debouncedCount = useDebounce(count, 1000);
    
  82. 
    
  83.   useCustomObject();
    
  84. 
    
  85.   const onClick = useCallback(
    
  86.     function onClick() {
    
  87.       updateCount(count + 1);
    
  88.     },
    
  89.     [count],
    
  90.   );
    
  91. 
    
  92.   // Tests nested custom hooks
    
  93.   useNestedOuterHook();
    
  94. 
    
  95.   // eslint-disable-next-line no-unused-vars
    
  96.   const contextValueB = useContext(ContextB);
    
  97. 
    
  98.   // Verify deep nesting doesn't break
    
  99.   useDeepHookA();
    
  100. 
    
  101.   return <button onClick={onClick}>Count: {debouncedCount}</button>;
    
  102. }
    
  103. const MemoWithHooks = memo(FunctionWithHooks);
    
  104. const ForwardRefWithHooks = forwardRef(FunctionWithHooks);
    
  105. 
    
  106. function wrapWithHoc(Component: (props: any, ref: React$Ref<any>) => any) {
    
  107.   function Hoc() {
    
  108.     return <Component />;
    
  109.   }
    
  110.   // $FlowFixMe[prop-missing]
    
  111.   const displayName = Component.displayName || Component.name;
    
  112.   // $FlowFixMe[incompatible-type] found when upgrading Flow
    
  113.   Hoc.displayName = `withHoc(${displayName})`;
    
  114.   return Hoc;
    
  115. }
    
  116. const HocWithHooks = wrapWithHoc(FunctionWithHooks);
    
  117. 
    
  118. export default function CustomHooks(): React.Node {
    
  119.   return (
    
  120.     <Fragment>
    
  121.       <FunctionWithHooks />
    
  122.       <MemoWithHooks />
    
  123.       <ForwardRefWithHooks />
    
  124.       <HocWithHooks />
    
  125.     </Fragment>
    
  126.   );
    
  127. }
    
  128. 
    
  129. // Below copied from https://usehooks.com/
    
  130. function useDebounce(value: number, delay: number) {
    
  131.   // State and setters for debounced value
    
  132.   const [debouncedValue, setDebouncedValue] = useState(value);
    
  133. 
    
  134.   // Show the value in DevTools
    
  135.   useDebugValue(debouncedValue);
    
  136. 
    
  137.   useEffect(
    
  138.     () => {
    
  139.       // Update debounced value after delay
    
  140.       const handler = setTimeout(() => {
    
  141.         setDebouncedValue(value);
    
  142.       }, delay);
    
  143. 
    
  144.       // Cancel the timeout if value changes (also on delay change or unmount)
    
  145.       // This is how we prevent debounced value from updating if value is changed ...
    
  146.       // .. within the delay period. Timeout gets cleared and restarted.
    
  147.       return () => {
    
  148.         clearTimeout(handler);
    
  149.       };
    
  150.     },
    
  151.     [value, delay], // Only re-call effect if value or delay changes
    
  152.   );
    
  153. 
    
  154.   return debouncedValue;
    
  155. }
    
  156. // Above copied from https://usehooks.com/