/
githubmr
/
facebook-react
Обзор
Документация
Войти
/
githubmr
/
facebook-react
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
main
packages/react-devtools-shell/src/app/InspectableElements/CustomHooks.js
258 строк
6 KB
Sam Zhou
[flow] Eliminate usage of more than 1-arg `React.AbstractComponent` in React codebase (#31314)
22 окт 2024, 02:17
Не верифицирован
22 окт 2024, 02:17
45804af
Код
Авторство
О чём код?
/** * 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, use, } from 'react'; import {useFormState, useFormStatus} 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$RefSetter<any>) { const [count, updateCount] = useState(0); // eslint-disable-next-line no-unused-vars const contextValueA = useContext(ContextA); useOptimistic<number, mixed>(1); use(ContextA); // 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 <button onClick={onClick}>Count: {debouncedCount}</button>; } const MemoWithHooks = memo(FunctionWithHooks); const ForwardRefWithHooks = forwardRef(FunctionWithHooks); function wrapWithHoc( Component: (props: any, ref: React$RefSetter<any>) => any, ) { function Hoc() { return <Component />; } // $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); const Suspendender = React.lazy(() => { return new Promise<any>(resolve => { setTimeout(() => { resolve({ default: () => 'Finished!', }); }, 3000); }); }); function Transition() { const [show, setShow] = React.useState(false); const [isPending, startTransition] = React.useTransition(); return ( <div> <React.Suspense fallback="Loading"> {isPending ? 'Pending' : null} {show ? <Suspendender /> : null} </React.Suspense> {!show && ( <button onClick={() => startTransition(() => setShow(true))}> Transition </button> )} </div> ); } function incrementWithDelay(previousState: number, formData: FormData) { const incrementDelay = +formData.get('incrementDelay'); const shouldReject = formData.get('shouldReject'); const reason = formData.get('reason'); return new Promise((resolve, reject) => { setTimeout(() => { if (shouldReject) { reject(reason); } else { resolve(previousState + 1); } }, incrementDelay); }); } function FormStatus() { const status = useFormStatus(); return <pre>{JSON.stringify(status)}</pre>; } function Forms() { const [state, formAction] = useFormState<any, any>(incrementWithDelay, 0); return ( <form> State: {state} <label> delay: <input name="incrementDelay" defaultValue={5000} type="text" inputMode="numeric" /> </label> <label> Reject: <input name="reason" type="text" /> <input name="shouldReject" type="checkbox" /> </label> <button formAction={formAction}>Increment</button> <FormStatus /> </form> ); } class ErrorBoundary extends React.Component<{children?: React$Node}> { state: {error: any} = {error: null}; static getDerivedStateFromError(error: mixed): {error: any} { return {error}; } componentDidCatch(error: any, info: any) { console.error(error, info); } render(): any { if (this.state.error) { return <div>Error: {String(this.state.error)}</div>; } return this.props.children; } } export default function CustomHooks(): React.Node { return ( <Fragment> <FunctionWithHooks /> <MemoWithHooks /> <ForwardRefWithHooks /> <HocWithHooks /> <Transition /> <ErrorBoundary> <Forms /> </ErrorBoundary> </Fragment> ); } // 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/