Skip to content

Instantly share code, notes, and snippets.

@Kelin2025
Last active September 6, 2023 02:20
Show Gist options
  • Save Kelin2025/e6ed7b8678c80cd9f5e43947aba96f87 to your computer and use it in GitHub Desktop.
Save Kelin2025/e6ed7b8678c80cd9f5e43947aba96f87 to your computer and use it in GitHub Desktop.
// CAUTION
// - Avoid dynamic factories calls
// - Generic factories are not supported in `useModel`and `modelView` components
'use client'
import { createFactory, invoke } from '@withease/factories'
import { Context, createContext, FC, Provider, 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>
}
/**
* 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 {} = {}>(
factory: T,
View: FC<Props & { model: ReturnType<typeof invoke<T, Parameters<T>[0]>> }>,
) => {
const Provider = createModelProvider(factory)
const Render = (props: Props & { model: ReturnType<typeof invoke<T, Parameters<T>[0]>> }) => {
return (
<Provider value={props.model}>
<View {...props} />
</Provider>
)
}
// `as` is used for a better "Go To Definition"
return Render as typeof View
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment