Last active
August 3, 2023 19:48
-
-
Save adnanalbeda/736fb089e9d5db0c15d8d4380daf02cf to your computer and use it in GitHub Desktop.
React Utility Components
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Component, ReactNode } from "react"; | |
interface ErrorBoundaryProps { | |
errorChildren?: JSX.Element | ReactNode; | |
children?: JSX.Element | ReactNode; | |
reloadOnError?: boolean; | |
} | |
class ErrorBoundary extends Component< | |
ErrorBoundaryProps, | |
{ hasError: boolean } | |
> { | |
constructor(props: ErrorBoundaryProps) { | |
super(props); | |
this.state = { hasError: false }; | |
} | |
static getDerivedStateFromError(error: unknown) { | |
console.error(error); // Update state so the next render will show the fallback UI. | |
return { hasError: true }; | |
} | |
// You can also log the error to an error reporting service here | |
componentDidCatch(error: unknown, errorInfo: unknown) { | |
console.error(error); | |
console.error(errorInfo); | |
} | |
render() { | |
if (this.state.hasError) { | |
if (this.props.reloadOnError) window.location.reload(); | |
return ( | |
this.props.errorChildren ?? ( | |
<span> | |
An error occurred causing failure to load this component. | |
<br /> | |
Check console for more details. | |
</span> | |
) | |
); | |
} | |
return this.props.children; | |
} | |
} | |
export default ErrorBoundary; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Fragment, useMemo } from "react"; | |
type Nullable = null | undefined; | |
type AllowNullable<T, TNullable extends boolean> = TNullable extends true | |
? T | |
: T extends Nullable | |
? never | |
: T; | |
interface ForProps<T, TNullable extends boolean> { | |
each?: T[] | null; | |
keys: (of: AllowNullable<T, TNullable>, i: number) => string; | |
allowNullables?: TNullable; | |
children: ( | |
prop: AllowNullable<T, TNullable>, | |
i: number | |
) => JSX.Element | JSX.Element[]; | |
} | |
export function For<T, TNullable extends boolean = false>({ | |
each, | |
keys, | |
allowNullables, | |
children, | |
}: ForProps<T, TNullable>) { | |
const items = useMemo<AllowNullable<T, TNullable>[]>( | |
() => | |
((allowNullables ? each : each?.filter((x) => x != null)) ?? | |
[]) as AllowNullable<T, TNullable>[], | |
[each, allowNullables] | |
); | |
if (!items.length) return null; | |
return ( | |
<> | |
{items.map((x, i) => ( | |
<Fragment key={keys(x, i)}>{children(x, i)}</Fragment> | |
))} | |
</> | |
); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export type XNode = JSX.Element | string | number | boolean | null | undefined; | |
export type XChild = XNode; | |
export type XC = XChild; | |
export type XCs = XChild[] | XChild; | |
export type ChildrenBuilder<T> = (options: T) => XC; | |
export type CB<T> = ChildrenBuilder<T>; | |
export type XC_CB<T> = ChildrenBuilder<T> | XCs; | |
export function buildChildren<T>(options: T, children?: XC_CB<T>) { | |
if (!children) return null; | |
if (typeof children === "function") return children(options); | |
return children; | |
} | |
export function BuildChildren<T>(props: { children?: XC_CB<T>; options: T }) { | |
return <>{buildChildren(props.options, props.children)}</>; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { JSX, ReactNode } from "react"; | |
// X: Static Return | |
// F: Functional Return | |
// define: MyComponent(props:FChildren<CustomProps>) | |
// return: return children(customProps) | |
// call: <MyComponent>{(props)=><>..rest..</>}</MyComponent> | |
// D: Dynamic Return (by 'as') | |
// define: MyComponent({as: As,...restProps}:DChildren<ElementsTags>) | |
// OR | |
// MyComponent({as: As,...restProps}:DChildren<"h1" | "h2">) | |
// return: return <As {...restProps} />; | |
// call: <MyComponent as="h1">..rest..</MyComponent> | |
// FD: Dynamic or Functional Dynamic Return (Same as D, but allow passing custom props) | |
// return: if (typeof As === "string") return <As {...restProps} />; | |
// return <As {...restProps} {...customProps} />; | |
// call: <MyComponent as={({children})=><>{children}</>}>..rest..</MyComponent> | |
// XF: Static, and optionally Functional Return | |
// return: return typeof children === "function" ? children(customProps) : children; | |
// XD: Static, and optionally Dynamic Return :: if (as) {/*do the D above*/} | |
// XFD: Static, and optionally Functional Dynamic Return :: if (as) {/*do the FD above*/} | |
export type ElementsTags = keyof JSX.IntrinsicElements; | |
export type XChildren<Props = Record<string, unknown>> = { | |
children?: ReactNode; | |
} & Props; | |
export type XElement< | |
T extends ElementsTags, | |
OM extends keyof JSX.IntrinsicElements[T] = never, | |
EXT = Record<string, unknown> | |
> = Omit<JSX.IntrinsicElements[T], OM> & EXT; | |
export type DElement< | |
T extends ElementsTags = ElementsTags, | |
OM extends keyof JSX.IntrinsicElements[T] = never, | |
EXT = Record<string, unknown> | |
> = { as: T } & Omit<JSX.IntrinsicElements[T], OM> & EXT; | |
export type FChildren<Param, Props = Record<string, unknown>> = { | |
children?: (value: Param) => ReactNode; | |
} & Props; | |
export type FDElement< | |
T extends ElementsTags = ElementsTags, | |
OM extends keyof JSX.IntrinsicElements[T] = never, | |
EXT = Record<string, unknown> | |
> = { as: T | FChildren<XElement<T, OM, EXT>> } & Omit< | |
JSX.IntrinsicElements[T], | |
OM | |
> & | |
EXT; | |
export type XFChildren<Param, Props = Record<string, unknown>> = { | |
children?: ((value: Param) => ReactNode) | ReactNode; | |
} & Props; | |
export type XDElement< | |
T extends ElementsTags = ElementsTags, | |
OM extends keyof JSX.IntrinsicElements[T] = never, | |
EXT = Record<string, unknown> | |
> = { as?: T } & Omit<JSX.IntrinsicElements[T], OM> & EXT; | |
export type XFDElement< | |
T extends ElementsTags = ElementsTags, | |
OM extends keyof JSX.IntrinsicElements[T] = never, | |
EXT = Record<string, unknown> | |
> = { as?: T | FChildren<XElement<T, OM, EXT>> } & Omit< | |
JSX.IntrinsicElements[T], | |
OM | |
> & | |
EXT; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { useEffect, useState } from "react"; | |
import { createPortal } from "react-dom"; | |
type PortalProps = { | |
id?: string; | |
as?: keyof HTMLElementTagNameMap; | |
children: React.ReactNode | React.ReactNode[]; | |
className?: string; | |
style?: Partial<CSSStyleDeclaration>; | |
}; | |
function Portal({ id, as, children, className, style }: PortalProps) { | |
const [portal, setPortal] = useState<HTMLElement>(); | |
useEffect(() => { | |
// create portal element | |
const el = document.createElement(as ?? "div"); | |
// apply common attributes | |
if (id) el.id = id; | |
if (className) el.className = className; | |
if (style) { | |
for (const k in style) { | |
if (style[k]) el.style[k] = style[k]!; | |
} | |
} | |
// append element to body | |
document.body.appendChild(el); | |
// set portal element forcing a rerender to Portal Element so it shows the content | |
setPortal(el); | |
// remove on unmount | |
return () => { | |
el.remove(); | |
}; | |
}, [id, className, style]); | |
if (!portal) return null; | |
return createPortal(children, portal); | |
} | |
export default Portal; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { useEffect } from "react"; | |
type UseEffectProps = { | |
effect: () => void | (() => void); | |
} & ( | |
| { | |
deps: unknown[]; | |
infinite?: undefined; | |
} | |
| { | |
deps?: undefined; | |
infinite: true; | |
} | |
); | |
export default function UseEffect({ deps, effect }: UseEffectProps) { | |
useEffect(effect, deps); | |
return null; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { | |
Dispatch, | |
SetStateAction, | |
useCallback, | |
useEffect, | |
useMemo, | |
useState, | |
} from "react"; | |
import { BuildChildren, XC_CB } from "../../utils/JSXChildren"; | |
type ChildrenAccessor<T> = { | |
state: [T, Dispatch<SetStateAction<T>>]; | |
get: <K extends keyof T>(prop: K) => T[K]; | |
set: <K extends keyof T>(prop: K, value: T[K]) => void; | |
}; | |
type UseObjectStateProps<T extends Object | Record<string | number, unknown>> = | |
{ | |
initialValue?: T; | |
reinitialize?: boolean; | |
children?: XC_CB<ChildrenAccessor<T>>; | |
}; | |
export default function UseObjectState< | |
T extends Object | Record<string | number, unknown> = any | |
>({ initialValue, reinitialize, children }: UseObjectStateProps<T>) { | |
const [getter, setter] = useState<T>(initialValue ?? ({} as T)); | |
const get = useCallback( | |
<K extends keyof T>(prop: K): T[K] => getter[prop], | |
[getter] | |
); | |
const set = useCallback(<K extends keyof T>(prop: K, value: T[K]) => { | |
setter((prev) => ({ ...prev, [prop]: value })); | |
}, []); | |
useEffect(() => { | |
if (reinitialize) setter(initialValue as T); | |
}, [initialValue, reinitialize]); | |
return ( | |
<BuildChildren options={{ state: [getter, setter], get, set }}> | |
{children} | |
</BuildChildren> | |
); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { useState, useRef } from "react"; | |
type ProxyRecord = Record<string | symbol, unknown>; | |
type ProxyType<T extends ProxyRecord> = T & { | |
__isProxy: boolean; | |
__reinitialize: (value: T) => void; | |
}; | |
const reactProxyHandler = <T extends ProxyRecord>( | |
render: () => void | |
): ProxyHandler<T> => ({ | |
get: (target, key: keyof T) => { | |
if (key === "__isProxy") return true; | |
if (key === "__reinitialize") | |
return (value: T) => { | |
Object.keys(target).forEach((key) => delete target[key]); | |
Object.assign(target, value); | |
render(); | |
}; | |
const prop = target[key]; | |
// return if property is not found, null or undefined | |
if (prop == null) return prop; | |
// set value as proxy if object | |
if ( | |
typeof prop === "object" && | |
!(prop as unknown as ProxyType<T>).__isProxy | |
) { | |
return (target[key] = new Proxy( | |
prop, | |
reactProxyHandler(render) as never | |
)); | |
} | |
return prop; | |
}, | |
set: (target, key, value) => { | |
// todo : call callback | |
target[key as keyof typeof target] = value; | |
render(); | |
return true; | |
}, | |
}); | |
export default function useProxyState<T extends ProxyRecord>( | |
initValue: T | |
): ProxyType<T> { | |
const [, __] = useState(false); | |
const render = useRef(() => __((v) => !v)).current; | |
return useRef(new Proxy(initValue, reactProxyHandler(render))) | |
.current as ProxyType<T>; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Dispatch, SetStateAction, useEffect, useState } from "react"; | |
import { BuildChildren, XC_CB } from "../../utils/JSXChildren"; | |
type UseStateProps<T> = { | |
initialValue?: T; | |
reinitialize?: boolean; | |
children?: XC_CB<[T | undefined, Dispatch<SetStateAction<T | undefined>>]>; | |
}; | |
export default function UseState<T>({ | |
initialValue, | |
reinitialize, | |
children, | |
}: UseStateProps<T>) { | |
const [getter, setter] = useState(initialValue); | |
useEffect(() => { | |
if (reinitialize) setter(initialValue); | |
}, [initialValue, reinitialize]); | |
return <BuildChildren options={[getter, setter]}>{children}</BuildChildren>; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment