As the error says, objects are not valid children in React. You're using the result of Promise.all
(a Promise object) as the result of that JSX expression:
{Promise.all(/*...*/)}
Instead, you need to wait for the promise from Promise.all
to be fulfilled with the array of results. You can't do that when rendering, you need to do it outside of rendering and then set state based on the result, rendering that state instead.
Here's a simple example (look at the code in the <script type="text/babel">
script block; sadly, Stack Snippets use an out of date version of Babel so we can't do it normally):
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
<script type="text/babel" data-presets="es2017,react">
const {useState, useEffect} = React;
// Stand-in for actual retriever function
function getThing(id, signal) {
// ^^^^^^−−−− this is so you can pass
// it to anything that supports signals to cancel the
// work if the component unmounts.
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(`Done getting thing #${id}`);
resolve({id});
}, Math.floor(Math.random() * 800));
});
}
const Thing = ({thing}) => <div>{thing.id}</div>;
const Example = () => {
const [things, setThings] = useState(null);
// Load on mount
useEffect(() => {
const controller = new AbortController();
const {signal} = controller;
(async () => {
try {
const things = await Promise.all([1, 2, 3].map(async (id) => {
console.log(`Getting thing #${id}...`);
const thing = await getThing(id, {signal});
return <Thing thing={thing} />;
}));
setThings(things);
} catch (error) {
// ...handle/report error...
console.error(error.message, error.stack);
}
})();
return () => {
controller.abort();
}
}, []); // [] = just on mount
// Return
return <div>
{things === null && <em>Loading...</em>}
{things}
</div>;
};
ReactDOM.render(<Example />, document.getElementById("root"));
</script>
<script src="https://unpkg.com/regenerator-runtime@0.13.2/runtime.js"></script>
<script src="https://unpkg.com/@babel/standalone@7.10.3/babel.min.js"></script>
That version stores actual React elements in state. You might instead store just the information you need (an object or whatever) and then render the elements as needed:
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
<script type="text/babel" data-presets="es2017,react">
const {useState, useEffect} = React;
// Stand-in for actual retriever function
function getThing(id, signal) {
// ^^^^^^−−−− this is so you can pass
// it to anything that supports signals to cancel the
// work if the component unmounts.
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(`Done getting thing #${id}`);
resolve({id});
}, Math.floor(Math.random() * 800));
});
}
const Thing = ({thing}) => <div>{thing.id}</div>;
const Example = () => {
const [things, setThings] = useState(null);
// Load on mount
useEffect(() => {
const controller = new AbortController();
const {signal} = controller;
(async () => {
try {
const things = await Promise.all([1, 2, 3].map(async (id) => {
console.log(`Getting thing #${id}...`);
const thing = await getThing(id, {signal});
return thing;
}));
setThings(things);
} catch (error) {
// ...handle/report error...
console.error(error.message, error.stack);
}
})();
return () => {
controller.abort();
}
}, []); // [] = just on mount
// Return
return <div>
{things === null && <em>Loading...</em>}
{things && things.map(thing => <Thing thing={thing} />)}
</div>;
};
ReactDOM.render(<Example />, document.getElementById("root"));
</script>
<script src="https://unpkg.com/regenerator-runtime@0.13.2/runtime.js"></script>
<script src="https://unpkg.com/@babel/standalone@7.10.3/babel.min.js"></script>
The map
callback also has issues. For instance, the true branch in if(channelMetaData?.isOneToOne){
doesn't return anything. You have a return
in a then
callback, but you don't have one in the map
callback. You may find it useful to use an async
function for the map
callback and use await
rather than .then
. That might look something like this:
// (In an `async` function)
const result = await Promise.all(channelList.map(async (channel) => {
// −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−^^^^^
console.log(channel);
const channelMetaData = JSON.parse(channel?.Metadata);
//If it is one-to-one chat
console.log(channelMetaData);
if (channelMetaData?.isOneToOne) {
const res = await listChannelMemberships(channel.ChannelArn, userId);
// −−−−−−−−−−−−−−−−−^^^^^
let otherPerson; // Declare it once (and `var` is deprecated)
if (res[0].Member.Name == userData.username) {
console.log("I am this user", res[0].Member.Name);
otherPerson = res[1].Member.Name;
} else if (res[1].Member.Name == userData.username) {
console.log("I am this user", res[1].Member.Name);
otherPerson = res[0].Member.Name;
}
console.log(otherPerson);
return <ChannelItem
key={channel.ChannelArn}
name={otherPerson}
actions={loadUserActions(userPermission.role, channel)}
isSelected={channel.ChannelArn === activeChannel.ChannelArn}
onClick={e => {
e.stopPropagation();
channelIdChangeHandler(channel.ChannelArn);
}}
unread={unreadChannels.includes(channel.ChannelArn)}
unreadBadgeLabel="New"
>
</ChannelItem>;
} else {
return <ChannelItem
key={channel.ChannelArn}
name={channel.Name}
actions={loadUserActions(userPermission.role, channel)}
isSelected={channel.ChannelArn === activeChannel.ChannelArn}
onClick={e => {
e.stopPropagation();
channelIdChangeHandler(channel.ChannelArn);
}}
unread={unreadChannels.includes(channel.ChannelArn)}
unreadBadgeLabel="New"
>
</ChannelItem>;
}
}));