Created
May 2, 2024 08:22
-
-
Save dovranJorayev/0f28f53c460c2f6653b1b44c9aad2bd2 to your computer and use it in GitHub Desktop.
effector model react bindings
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 { | |
ComponentType, | |
ForwardRefExoticComponent, | |
PropsWithoutRef, | |
RefAttributes, | |
createContext, | |
forwardRef, | |
useContext | |
} from 'react'; | |
export type ComponentLike<P> = (props: P) => JSX.Element | null; | |
/** | |
* Used to create unsafe defaults for typescript types | |
* that need to be replaced with real values | |
* before the value was consumed | |
* | |
* @param error error message for trowed error | |
* @returns unsafe value | |
* @throws Error instance with error message that was passed as an argument | |
*/ | |
export const makeUnsafeDefaults = <T extends object>(error: string): T => { | |
return new Proxy<T>({} as never, { | |
get: () => { | |
throw new Error(error); | |
} | |
}); | |
}; | |
export function isForwardRefComponent<P>( | |
component: ComponentLike<P> | ComponentType<P> | ForwardRefExoticComponent<P> | |
): component is ForwardRefExoticComponent<P> { | |
return ( | |
(component as ForwardRefExoticComponent<P>).$$typeof === | |
Symbol.for('react.forward_ref') | |
); | |
} | |
export function makeModelContext<M extends object = object>( | |
createModel?: (...params: any[]) => M, | |
modelName = 'AnonymousModel' | |
) { | |
const Ctx = createContext<M>( | |
makeUnsafeDefaults(`Model of ${modelName} instance was not provided`) | |
); | |
function withProvider<Props, _Ref = never>( | |
Component: ComponentType<Props> | |
): ComponentType<Props & { model: M }>; | |
function withProvider<Props, _Ref = never>( | |
Component: ComponentLike<Props> | |
): ComponentLike<Props & { model: M }>; | |
function withProvider<Props, Ref>( | |
Component: ForwardRefExoticComponent< | |
PropsWithoutRef<Props> & RefAttributes<Ref> | |
> | |
): ForwardRefExoticComponent< | |
PropsWithoutRef<Props & { model: M }> & RefAttributes<Ref> | |
>; | |
function withProvider<P extends { model: M }>( | |
ComponentLike: | |
| ComponentLike<P> | |
| ComponentType<P> | |
| ForwardRefExoticComponent<P> | |
): any { | |
if (isForwardRefComponent(ComponentLike)) { | |
return forwardRef(function ForwardRefWithModelProvider( | |
props: P, | |
ref: unknown | |
) { | |
return ( | |
<Ctx.Provider value={props.model}> | |
<ComponentLike {...props} ref={ref} /> | |
</Ctx.Provider> | |
); | |
}); | |
} else { | |
return function WithModelProvider(props: P) { | |
return ( | |
<Ctx.Provider value={props.model}> | |
<ComponentLike {...props} /> | |
</Ctx.Provider> | |
); | |
}; | |
} | |
} | |
function withModelInstance( | |
model: M, | |
displayName?: string | |
): <Props = Record<string, unknown>>( | |
Component: ComponentLike<Props> | |
) => ComponentLike<Props>; | |
function withModelInstance( | |
model: M, | |
displayName?: string | |
): <Props>(Component: ComponentType<Props>) => ComponentType<Props>; | |
function withModelInstance(model: M, displayName = 'ModelInjectedComponent') { | |
return <P, C extends ComponentLike<P> | ComponentType<P>>(Component: C) => { | |
const ModelInjectedComponent = (props: P) => { | |
return ( | |
<Ctx.Provider value={model}> | |
<Component {...(props as any)} /> | |
</Ctx.Provider> | |
); | |
}; | |
Object.assign(ModelInjectedComponent, { displayName }); | |
return ModelInjectedComponent; | |
}; | |
} | |
const useModel = () => useContext(Ctx); | |
return { | |
useModel, | |
withProvider, | |
withModelInstance | |
}; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.