import React, { createContext, useContext, useMemo } from 'react' export type ContextualProps = { Context: React.Context> PassProps: ({ children, ...rest }: Partial> & { children?: any }>) => JSX.Element useProps>(componentProps?: B): B extends undefined ? A : B & A Reset(props: { children: any }): any useReset(children: A): A } export function createContextualProps(defaults?: A): ContextualProps { const Context = createContext | null>(null) const PassProps = ({ children, ...rest }: Partial & { children?: any }>) => { const parentProps = useContext(Context) // @ts-ignore const val = { ...defaults, ...parentProps, ...rest } const memoVal = useMemo( () => val, // memo based on values [...Object.keys(val).map(k => val[k])], ) return {children} } const useReset = (children: any) => { const extraProps = useContext(Context) if (extraProps) { return {children} } return children } return { Context, PassProps, useProps | undefined>( componentProps?: B, ): B extends undefined ? A : B & A { const extra = useContext(Context) return useMemo(() => { if (!extra) { return componentProps as any } if (!componentProps) { return extra } // merge just undefined componentProps from extra const final: any = { ...componentProps } for (const key in extra) { if (final[key] === undefined) { final[key] = extra[key] } } return final as any }, [extra, componentProps]) }, Reset({ children }) { return useReset(children) }, useReset, } }