Created
October 3, 2023 11:49
-
-
Save finom/0908a7390ca7946203f52fa7ba6e7da4 to your computer and use it in GitHub Desktop.
Allows to create portals within react components
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
import React, { | |
createContext, | |
useState, | |
useContext, | |
ReactNode, | |
FunctionComponent, | |
Dispatch, | |
SetStateAction, | |
} from 'react'; | |
// Define a type for the shared state context | |
interface PortalContextProps<T> { | |
props: T | null; | |
setProps: Dispatch<SetStateAction<T | null>>; | |
} | |
// Create a context with an initial value of undefined | |
// eslint-disable-next-line @typescript-eslint/no-explicit-any | |
const PortalContext = createContext<PortalContextProps<any> | undefined>(undefined); | |
export default function createComponentPortal<T>() { | |
const Provider: FunctionComponent<{ children: ReactNode }> = ({ children }) => { | |
const [props, setProps] = useState<T | null>(null); | |
return <PortalContext.Provider value={{ props, setProps }}>{children}</PortalContext.Provider>; | |
}; | |
const Manipulator: FunctionComponent<T> = (props) => { | |
const context = useContext(PortalContext); | |
if (context) context.setProps(props); // Update the shared state when Manipulator receives new props | |
return null; // Render nothing | |
}; | |
const Renderer: FunctionComponent<{ children: (props: T) => ReactNode }> = ({ children }) => { | |
const context = useContext(PortalContext); | |
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument | |
if (context && context.props) return children(context.props); // Render the children function with the shared props | |
return null; // Render nothing if no props have been set yet | |
}; | |
return { Provider, Manipulator, Renderer }; | |
} | |
/* | |
// Usage: | |
const { Provider, Manipulator, Renderer } = createComponentPortal<{ message: string }>(); | |
function App() { | |
return ( | |
<Provider> | |
<Renderer>{(props) => <div {...props}>{props.message}</div>}</Renderer> | |
<Manipulator message="Hello World" /> | |
</Provider> | |
); | |
} | |
*/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment