There are many suggested solutions to this type of problem on Stackoverflow, but none of them works for my code. However, none of the other problems involve nested dynamic imports, so my condition may be unique:
I have a component in NextJS that relies on nested dynamic importation of modules. One module contains a listing of other modules to import, and those modules contain the data I need to construct the JSX.
Using NextJS 13, this works perfectly, perhaps because it is being rendered server-side.
Using NextJS 12, which renders client-side, it fails with this error message, which has been reported many times on Stackoverflow and elsewhere:
Unhandled Runtime Error Error: Objects are not valid as a React child (found: [object Promise]). If you meant to render a collection of children, use an array instead.
I've tried encapsulating this routine in useEffect:
const [data, setData] = useState([]);
useEffect(() => {
async function fetchData() {
const xyz = await [return from asychronous function, in my case a dynamic import]
setData(xyz);
}
fetchData();
}, []);
return <>{data}</>
...but it also fails. Any clue how to solve this for NextJS 12? Again, this works in NextJS 13, which performs this code server-side.
Here's a distillation of the code:
export default function Main() {
let workNav = [];
// theme is the name of the module, and is generated dynamically by code not included
// here for brevity's sake
// {theme}.mjs contains an object with an array of work names:
return import(`./${theme}.mjs`).then(function ({ default: themeObject }) {
return Promise.all(
// Each {work} is a name of a module:
themeObject.works.map(function (work, index) {
import(`./${work}.mjs`).then(function ({
default: workObject,
}) {
workNav.push(
<li key={work + index}>
<Link href={themeObject.theme + "/" + work}>{work} | {workObject.message}</Link>
</li>
);
});
})
).then(function () {
return (
<>
<ul className="text-align-center">{workNav}</ul>
</>
);
});
});
}
The code you provided is not reproducible, and I honestly have never used an import()
function. But maybe I can still help... It seems that all you want to do is to chain some promises, and then render elements.
I tried to make a similar situation with some dummy imports, check if it can help you:
import Link from 'next/link';
import React, { useEffect, useState } from 'react'
export default function Main() {
const [workNav, setWorkNav] = useState([]);
//just to simulate the real import promise
const import1 = () => {
return new Promise(resolve => {
setTimeout(() => {
resolve(
{
default: {
theme: "some-theme",
works: [
"first-work",
"second-work",
"third-work",
"fourth-work"
]
}
})
}, Math.random() * 3000);
})
}
const import2 = () => {
return new Promise(resolve => {
setTimeout(() => {
resolve(
{
default: {
message: "Some message " + Math.random()
}
})
}, Math.random() * 3000);
})
}
useEffect(() => {
const workNavTemp = [];
import1("theme.mjs").then(({ default: themeObject }) => {
return Promise.all(
themeObject.works.map((work, index) => import2(work).then(({
default: workObject
}) => {
workNavTemp.push(
<li key={work + index}>
<Link href={themeObject.theme + "/" + work}>
<a>
{work} | {workObject.message}
</a>
</Link>
</li>)
})
)).then(() => {
setWorkNav(workNavTemp);
})
})
}, [])
return (
<>
{workNav.length ? workNav : "Loading, please wait..."}
</>
)
}