Created
September 28, 2022 02:30
-
-
Save itsmunim/0d114b1852a3c73edff2ef8f03fc2c50 to your computer and use it in GitHub Desktop.
A render helper for react components, supporting SSR too
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 { createElement as e } from 'react'; | |
import { createRoot, hydrateRoot } from 'react-dom/client'; | |
/** | |
* All the root container refs have to be maintained for re-rendering purpose. | |
*/ | |
const rootMap = {}; | |
/** | |
* For id less root elements, this will generate a specific prefixed id. | |
*/ | |
const randomId = ((i) => { | |
return () => { | |
return 'widget-root-' + ++i; | |
}; | |
})(0); | |
const getContainer = (rootElement) => { | |
if (!rootElement) { | |
throw new Error('Invalid root element. Element id or element itself has to be provided'); | |
} | |
const container = typeof rootElement === 'string' ? document.getElementById(rootElement) : rootElement; | |
container.id = container.id || randomId(); | |
return container; | |
}; | |
/** | |
* Helper method to render a widget instance created using `createWidgetInstance` | |
* | |
* @param widgetInstance Created widget instance using `createWidgetInstance` | |
* @param rootElement The root in dom, where this widget will be rendered. Can be an id or the element itself | |
* @param isSSR If the widget was rendered from server side, this will make sure it get hydrated properly on client side | |
* | |
* @example | |
* | |
* ```javascript | |
* const instance = createWidgetInstance(TabNavItem, {}); | |
* | |
* renderWidgetInstance(instance, 'navbar'); | |
* ``` | |
*/ | |
export const renderWidgetInstance = (widgetInstance, rootElement, isSSR) => { | |
const container = getContainer(rootElement); | |
let root = rootMap[container.id]; | |
if (!root) { | |
root = isSSR ? hydrateRoot(container, widgetInstance) : createRoot(container); | |
} | |
root.render(widgetInstance); | |
rootMap[container.id] = root; | |
}; | |
/** | |
* To unmount an widget instance from the root. | |
* | |
* @param rootElement The root in dom, where this widget will be rendered. Can be an id or the element itself | |
* | |
* @example | |
* | |
* ```javascript | |
* const instance = createWidgetInstance(TabNavItem, {}); | |
* renderWidgetInstance(instance, 'navbar'); | |
* ... | |
* | |
* unmount('navbar'); | |
* ``` | |
*/ | |
export const unmount = (rootElement) => { | |
const container = getContainer(rootElement); | |
const root = rootMap[container.id]; | |
root.unmount(); | |
delete rootMap[container.id]; | |
}; | |
/** | |
* Helper function to create widget instance with props and children inside, | |
* which can then be passed as children to other widgets as well or even rendered separately. | |
* e.g. renderWidgetInstance | |
* | |
* @param widget A widget from `Widgets` js bundle exposed into `window` | |
* @param props The attributes to render the widget, refer widget specific reference | |
* @param children The children widgets/components we want to render inside this widget | |
* @returns The widget react element | |
*/ | |
export const createWidgetInstance = (widget, props, children = null) => { | |
return e(widget, props, children); | |
}; | |
/** | |
* Helper function to render the widgets in a non-react setup like a plain js based html, php etc. | |
* | |
* You need to call this everytime the passed `props` object has any changes. `ReactDOM` won't be | |
* re-rendering unless the values in `props` object is really changed, so no performance concerns. | |
* | |
* @param widget A widget from `Widgets` js bundle exposed into `window` | |
* @param props The attributes to render the widget, refer widget specific reference | |
* @param container The container ID in dom, where this widget will be rendered or even the element itself is acceptable | |
* @param isSSR If the widget was rendered from server side, this will make sure it get hydrated properly on client side | |
* | |
* @example | |
* | |
* ```javascript | |
* const {renderWidget, Header} = Widgets; | |
* | |
* const onSearch = (text) => { | |
* // do whatever you want | |
* } | |
* const props = { | |
* title: 'Amazing!', | |
* navLinks: [{title: 'Nav 1', href: '#link'}], | |
* onSearch | |
* }; | |
* | |
* renderWidget(Header, props, 'header'); | |
* | |
* // or | |
* | |
* renderWidget(Header, props, document.getElementsByClassName('header')[0]); | |
* ``` | |
*/ | |
export const renderWidget = (widget, props, container, isSSR = false) => { | |
renderWidgetInstance(createWidgetInstance(widget, props), container, isSSR); | |
}; | |
/** | |
* Helper function to render the widgets in a non-react setup like a plain js based html, php etc. | |
* | |
* You need to call this everytime the passed `props` object has any changes. `ReactDOM` won't be | |
* re-rendering unless the values in `props` object is really changed, so no performance concerns. | |
* | |
* @param widget A widget from `Widgets` js bundle exposed into `window` | |
* @param props The attributes to render the widget, refer widget specific reference | |
* @param children The children widgets/components we want to render inside this widget | |
* @param container The container ID in dom, where this widget will be rendered or even the element itself is acceptable | |
* @param isSSR If the widget was rendered from server side, this will make sure it get hydrated properly on client side | |
* | |
* @example | |
* | |
* ```javascript | |
* const {renderWidgetWithChildren, createWidgetInstance, TabNav, TabNavItem} = Widgets; | |
* | |
* const props = { | |
* title: 'Nav Title!', | |
* ... | |
* }; | |
* | |
* const children = navItems.map(item => createWidgetInstance(TabNavItem, {})); | |
* | |
* renderWidgetWithChildren(TabNav, props, children, 'navbar'); | |
* | |
* // or | |
* | |
* renderWidgetWithChildren(TabNav, props, children, document.getElementsByClassName('navbar')[0]); | |
* ``` | |
*/ | |
export const renderWidgetWithChildren = (widget, props, children, container, isSSR = false) => { | |
renderWidgetInstance(createWidgetInstance(widget, props, children), container, isSSR); | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment