/** * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * * @flow */ import * as React from 'react'; import { createContext, forwardRef, Fragment, memo, useCallback, useContext, useDebugValue, useEffect, useOptimistic, useState, } from 'react'; import {useFormState} from 'react-dom'; const object = { string: 'abc', number: 123, boolean: true, null: null, undefined: undefined, array: ['a', 'b', 'c'], object: {foo: 1, bar: 2, baz: 3}, }; function useNestedInnerHook() { return useState(123); } function useNestedOuterHook() { return useNestedInnerHook(); } function useCustomObject() { useDebugValue(object); return useState(123); } function useDeepHookA() { useDebugValue('useDeepHookA'); useDeepHookB(); } function useDeepHookB() { useDebugValue('useDeepHookB'); useDeepHookC(); } function useDeepHookC() { useDebugValue('useDeepHookC'); useDeepHookD(); } function useDeepHookD() { useDebugValue('useDeepHookD'); useDeepHookE(); } function useDeepHookE() { useDebugValue('useDeepHookE'); useDeepHookF(); } function useDeepHookF() { useDebugValue('useDeepHookF'); } const ContextA = createContext('A'); const ContextB = createContext('B'); function FunctionWithHooks(props: any, ref: React$Ref) { const [count, updateCount] = useState(0); // eslint-disable-next-line no-unused-vars const contextValueA = useContext(ContextA); useOptimistic(1); // eslint-disable-next-line no-unused-vars const [_, __] = useState(object); // Custom hook with a custom debug label const debouncedCount = useDebounce(count, 1000); useCustomObject(); const onClick = useCallback( function onClick() { updateCount(count + 1); }, [count], ); // Tests nested custom hooks useNestedOuterHook(); // eslint-disable-next-line no-unused-vars const contextValueB = useContext(ContextB); // Verify deep nesting doesn't break useDeepHookA(); return ; } const MemoWithHooks = memo(FunctionWithHooks); const ForwardRefWithHooks = forwardRef(FunctionWithHooks); function wrapWithHoc(Component: (props: any, ref: React$Ref) => any) { function Hoc() { return ; } // $FlowFixMe[prop-missing] const displayName = Component.displayName || Component.name; // $FlowFixMe[incompatible-type] found when upgrading Flow Hoc.displayName = `withHoc(${displayName})`; return Hoc; } const HocWithHooks = wrapWithHoc(FunctionWithHooks); function Forms() { const [state, formAction] = useFormState((n: number, formData: FormData) => { return n + 1; }, 0); return (
{state}
); } export default function CustomHooks(): React.Node { return ( ); } // Below copied from https://usehooks.com/ function useDebounce(value: number, delay: number) { // State and setters for debounced value const [debouncedValue, setDebouncedValue] = useState(value); // Show the value in DevTools useDebugValue(debouncedValue); useEffect( () => { // Update debounced value after delay const handler = setTimeout(() => { setDebouncedValue(value); }, delay); // Cancel the timeout if value changes (also on delay change or unmount) // This is how we prevent debounced value from updating if value is changed ... // .. within the delay period. Timeout gets cleared and restarted. return () => { clearTimeout(handler); }; }, [value, delay], // Only re-call effect if value or delay changes ); return debouncedValue; } // Above copied from https://usehooks.com/