UNPKG

@wrdagency/react-islands

Version:

Created by Kyle Cooper at [WRD.agency](https://webresultsdirect.com)

103 lines (99 loc) 3.15 kB
import { jsx } from 'react/jsx-runtime'; import { createRoot } from 'react-dom/client'; import { useRef, useEffect } from 'react'; /** * Internal component for rendering raw HTML in a React component. */ function RawHTML({ html }) { const ref = useRef(null); // important to not have ANY deps useEffect(() => { if (ref.current) { ref.current.outerHTML = html; } }, []); return jsx("script", { ref: ref }); } /** * Create a higher-order component with certain fixed. * * Useful for quickly creating multiple variants of the same component to use as islands. * * @param component FC<T> * @param setProps Partial<T> * @returns FC<T> */ function withProps(component, setProps) { return (props) => { return component({ ...props, ...setProps }); }; } /** * Checks if the current script is running in a pre-render. * * @returns boolean */ function isServer() { return !(typeof window != "undefined" && window.document); } class Island { component; renderOptions; constructor(component, opts = {}) { this.component = component; this.renderOptions = { shouldHydrate: opts.shouldHydrate ?? true, multiple: opts.multiple ?? false, keepChildren: opts.keepChildren ?? false, }; } getProps(element) { const { keepChildren } = this.renderOptions; let props = {}; try { const json = element.dataset.props || "{}"; if (json) { const parsed = JSON.parse(json); if (typeof parsed !== "object" || Array.isArray(parsed)) { throw new Error(`Parsed JSON is not a valid dictionary object: '${json}'`); } if (parsed) { // Ignore null. props = parsed; } } } catch (e) { console.warn("Could not parse JSON props for React Island."); console.error(e); } if (keepChildren) { props.children = jsx(RawHTML, { html: element.innerHTML }); } return props; } getRoots(name) { const selector = `[data-island="${name}"]`; const { multiple } = this.renderOptions; const nodes = [...document.querySelectorAll(selector)]; if (!nodes) { console.warn(`Could not render React Island because DOM node (${selector}) could not be found.`); return []; } if (nodes.length > 1 && !multiple) { console.warn(`Multiple elements matched React Island selector (${selector}) but multiple was not enabled. Choosing first element as root.`); return [nodes[0]]; } return nodes; } render(name) { this.getRoots(name).forEach((element) => { const props = this.getProps(element); const Component = withProps(this.component, props); const root = createRoot(element); root.render(jsx(Component, {})); return root; }); } } export { Island, isServer, withProps };