Make a React introvert Component (What?🤨)

RMAG news

What the heck does that mean?
Today I’m going to break a React feature that I’ve been using for a while now.

The React.lazy function is mainly for lazy importing of external components, and in general, the official documentation will tell you to write it like this:

export const MyExternalComponent = React.lazy(() =>
import(./path/to/my/component)
)

This is a popular optimization recommendation that wraps a <Suspense> component around a component declared with the export default statement in an external component file and imports it on the first call to save resources.

Right. It must be the same component that you declared with the export default statement.

As you’ll see when you write a dynamic import statement, if you want to import something that you’ve declared as default export, you import it with the .default attribute.

export const MyExternalComponent = React.lazy(() =>
import(./path/to/my/component)
.then(({ OtherComponent }) => ({ default: OtherComponent }))
)

From this feature, I came up with a brilliant idea.

export const IntrovertComponent = React.lazy(() =>
fetch(/path/to/api)
.then(res => res.json())
.then(json => {
const MyComponent = () => {
return <pre>{JSON.stringify(json, null, 2)}</pre>
}
return { default: MyComponent }
})

You might be wondering if the above code will work, right?
The short answer is yes. It will work.

Why?

React doesn’t care if there’s an import statement in the function body or not. We’re not necessarily using the Webpack bundler, so it’s a pain for them to analyze the bundler’s code output.
Instead, the value is in the ability to defer the import statement.

Anyway, the argument to the lazy function accepts a function that returns a Promise instance, so it doesn’t have to be an import statement, just a Promise object.

Instead, there is one thing to keep in mind.

This behavior will be out of the purpose of the lazy function.
Its original purpose is to optimize component resources with import, but since you declared this code inline, you’ll be far from optimizing resources.

I hope that in the upcoming React 19, do not implement this nonsense and create a normal component with a use hook function.

The dynamic provided by Next.js can be implemented in the same way. In particular, since the initialization function of the Plotly library I was using returns a Promise, I implemented the following to create a pure client component away from SSR.

export const Plotly = dynamic(
() =>
import(plotly.js/dist/plotly.js).then(({ newPlot, purge }) => {
const Plotly = forwardRef(({ id, className, data, layout, config }, ref) => {
const originId = useId();
const realId = id || originId;
const originRef = useRef(null);
const [handle, setHandle] = useState(undefined);

useEffect(() => {
let instance;
originRef.current &&
newPlot(originRef.current!, data, layout, config).then((ref) => setHandle((instance = ref)));
return () => {
instance && purge(instance);
};
}, [data]);

useImperativeHandle(
ref,
() => (handle ?? originRef.current ?? document.createElement(div)),
[handle]
);

return <div id={realId} ref={originRef} className={className}></div>;
});
Plotly.displayName = Plotly;
return Plotly;
}),
{ ssr: false }
);

I named this component an introvert component.
Funny, huh?

Happy React’ing!