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);
const contextValueA = useContext(ContextA);
useOptimistic<number, mixed>(1);
use(ContextA);
const [_, __] = useState(object);
const debouncedCount = useDebounce(count, 1000);
useCustomObject();
const onClick = useCallback(
function onClick() {
updateCount(count + 1);
},
[count],
);
useNestedOuterHook();
const contextValueB = useContext(ContextB);
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 />;
}
const displayName = Component.displayName || Component.name;
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>
);
}
function useDebounce(value: number, delay: number) {
const [debouncedValue, setDebouncedValue] = useState(value);
useDebugValue(debouncedValue);
useEffect(
() => {
const handler = setTimeout(() => {
setDebouncedValue(value);
}, delay);
return () => {
clearTimeout(handler);
};
},
[value, delay],
);
return debouncedValue;
}