Forked from Kelin2025/withease-factories-react.tsx
Created
September 6, 2023 02:20
-
-
Save 7iomka/557a7710a6165db288bd2b73624201e0 to your computer and use it in GitHub Desktop.
This file contains 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
// CAUTION | |
// - Avoid dynamic factories calls | |
// - Generic factories are not supported in `useModel`and `modelView` components | |
// Usage: | |
// const Root = modelView(factory, () => { ... }) | |
// const model = useModel(factory) | |
// ... const $$instance = invoke(createSome, {someParams}) | |
'use client'; | |
import { createFactory } from '@withease/factories'; | |
import type { invoke } from '@withease/factories'; | |
import type { ComponentType, Context, Provider } from 'react'; | |
import { createContext, useContext } from 'react'; | |
const contexts = new Map< | |
ReturnType<typeof createFactory>, | |
Context<ReturnType<typeof invoke<ReturnType<typeof createFactory>>>> | |
>(); | |
export const createModelProvider = <T extends (props: any) => any>(factory: T) => { | |
contexts.set(factory, createContext(null)); | |
return contexts.get(factory)!.Provider as Provider<ReturnType<typeof invoke<T>>>; | |
}; | |
export const useModel = <T extends (props: any) => any>(factory: T) => { | |
const model = useContext(contexts.get(factory)!); | |
if (!model) { | |
throw new Error('No model found'); | |
} | |
return model as ReturnType<T>; | |
}; | |
// Helper type for model prop | |
export type FactoryModelType<T extends (props: any) => any> = ReturnType<typeof invoke<T, any>>; | |
// declare function invoke<C extends (...args: any) => any>(factory: C): OverloadReturn<void, OverloadUnion<C>>; | |
// declare function invoke<C extends (...args: any) => any, P extends OverloadParameters<C>[0]>(factory: C, params: P): OverloadReturn<P, OverloadUnion<C>>; | |
/** | |
* HOC that wraps your `View` into model `Provider`. Also adds `model` prop that will be passed into `Provider` | |
* @param factory Factory that will be passed through Context | |
* @param View Root component that will be wrapped into Context | |
* @returns Wrapped component | |
*/ | |
export const modelView = <T extends (props: any) => any, Props extends object = object>( | |
factory: T, | |
View: ComponentType<Props>, | |
) => { | |
const Provider = createModelProvider(factory); | |
// TODO: Find a workaround to use FactoryModelType as model type without breaking generics | |
const Render = ({ model, ...restProps }: Props & { model: any }) => { | |
return ( | |
<Provider value={model}> | |
<View {...(restProps as Props)} /> | |
</Provider> | |
); | |
}; | |
// `as` is used for a better "Go To Definition" | |
return Render; | |
}; | |
export { createFactory }; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment