uikit/src/remote/ui/_core/createRemoteElementComponent.jsx
Mohamadzadeh c75420a91c
All checks were successful
Build and Deploy Next.js + Nginx Docker Image / build-and-deploy (push) Successful in 50s
Build and Deploy Next.js + Nginx Docker Image / deploy (push) Successful in 7s
important changes
2026-07-14 16:33:32 +03:30

118 lines
2.6 KiB
JavaScript

"use client";
import React, { useEffect, useRef } from "react";
import { REMOTE_DEPS_KEY } from "./constants";
import { setupUikitRemoteDeps } from "./setupRemoteDeps";
const remotePromises = new Map();
function hasRequiredDeps(requiredDeps = []) {
if (typeof window === "undefined") return false;
const deps = window[REMOTE_DEPS_KEY];
return requiredDeps.every((key) => deps?.[key]);
}
function ensureRemoteDeps(requiredDeps = []) {
if (typeof window === "undefined") return;
if (!hasRequiredDeps(requiredDeps)) {
setupUikitRemoteDeps();
}
}
function loadRemote(componentName, remoteFileUrl) {
const cacheKey = `${componentName}:${remoteFileUrl}`;
if (!remotePromises.has(cacheKey)) {
remotePromises.set(
cacheKey,
import(
/* webpackIgnore: true */
/* @vite-ignore */
remoteFileUrl
).catch((error) => {
remotePromises.delete(cacheKey);
throw error;
}),
);
}
return remotePromises.get(cacheKey);
}
export function createRemoteElementComponent({
componentName,
tagName,
defaultRemoteUrl,
requiredDeps,
normalizeProps,
}) {
return function RemoteElementComponent({
theme,
chakraProviderProps,
...props
}) {
const containerRef = useRef(null);
const elementRef = useRef(null);
const latestPropsRef = useRef(null);
latestPropsRef.current = {
...(normalizeProps ? normalizeProps(props) : props),
...(theme !== undefined ? { theme } : {}),
...(chakraProviderProps ? { chakraProviderProps } : {}),
};
useEffect(() => {
let active = true;
ensureRemoteDeps(requiredDeps);
loadRemote(componentName, defaultRemoteUrl)
.then(() => {
if (!active) return;
if (!containerRef.current) return;
const element = document.createElement(tagName);
element.componentProps = latestPropsRef.current;
containerRef.current.appendChild(element);
elementRef.current = element;
})
.catch((error) => {
console.error(
`[uikit/remote/ui] Failed to load ${componentName}:`,
error,
);
});
return () => {
active = false;
if (elementRef.current) {
elementRef.current.remove();
elementRef.current = null;
}
};
}, [componentName, defaultRemoteUrl, tagName]);
useEffect(() => {
if (!elementRef.current) return;
elementRef.current.componentProps = latestPropsRef.current;
});
return (
<span
ref={containerRef}
style={{
display: "contents",
}}
/>
);
};
}