I’m using shadcn/ui with Radix UI’s Collapsible to make a collapsible filter section.
Wrapper component:
import { ReactNode, useEffect, useState } from "react";
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "./ui/collapsible";
import { Button } from "./ui/button";
import { ChevronDown } from "lucide-react";
import { cn } from "@/lib/utils";
export function CollapsibleFilter({
title,
children,
tmp,
}: {
title: string;
children: ReactNode;
tmp?: any;
}) {
const [open, setOpen] = useState(false);
useEffect(() => {
console.log("children", children);
console.log("tmp", tmp);
}, [open]);
return (
<Collapsible open={open} onOpenChange={setOpen}>
<CollapsibleTrigger asChild>
<Button
variant="ghost"
size="icon"
className="w-full justify-between text-base font-normal hover:bg-transparent!"
>
{title}
<ChevronDown
className={cn(
open && "rotate-180",
"transition-transform ease-in-out duration-200"
)}
/>
</Button>
</CollapsibleTrigger>
<CollapsibleContent className="flex flex-col gap-4 py-3">
{children}
</CollapsibleContent>
</Collapsible>
);
}
In my page I try to render children from a Map:
import { CollapsibleFilter } from "@/components/collapsible-filter";
import { useEffect, useState } from "react";
export default function Home() {
const [mapObj, setMap] = useState<Map<string, string>>();
useEffect(() => {
setMap(new Map([["John", "abc"]]));
}, []);
return (
<div>
<h2 className="text-red-800">
using <code>Array.from(map.entries())</code>
</h2>
<CollapsibleFilter title="test1">
{Array.from(mapObj?.entries() ?? []).map((kv) => {
return (
<div className="text-red-400">
<p>name:{kv[0]}</p>
<p>tag:{kv[1]}</p>
</div>
);
})}
</CollapsibleFilter>
<h2 className="text-blue-800">
using <code>map.entries()</code>
</h2>
<CollapsibleFilter title="test2">
{mapObj?.entries().map((kv) => {
return (
<div className="text-blue-300">
<p>name:{kv[0]}</p>
<p>tag:{kv[1]}</p>
</div>
);
})}
</CollapsibleFilter>
<h2>outside of collapsible filter</h2>
{mapObj?.entries().map((kv) => {
return (
<div className="text-orange-400">
<p>name:{kv[0]}</p>
<p>tag:{kv[1]}</p>
</div>
);
})}
</div>
);
}
In test1 (Array.from()) the items render fine. In test2 (mapObj.entries() directly) nothing renders inside the collapsible. But the same {mapObj?.entries().map(...)} expression outside of <CollapsibleFilter> works.
What it looks like:
Why does passing the result returned by mapObj.entries().map as children to CollapsibleFilter render nothing, but passing an array from Array.from(mapObj.entries()).map works? And why does the iterator version still work if I put it outside of the collapsible component?
First I have a custom component CollapsibleFilter which acts as a wrapper of Collapsible from shadcn/ui. By passing this component with children it will display the content of children when the collapsible is opened. When I write:
<CollapsibleFilter title="foo">
hello world
</CollapsibleFilter>
it should show this in the browser (click the collapsible):
I created a React state variable mapObj:
const [mapObj, setMap] = useState<Map<string, string>>();
useEffect(() => {
setMap(new Map([["John", "abc"]]));
}, []);
Which is an object of type Map and will be initialized with a key value pair of ["John","abc"]. When I write:
<h2>outside of collapsible filter</h2>
{mapObj?.entries().map((kv) => {
return (
<div className="text-orange-400">
<p>name:{kv[0]}</p>
<p>tag:{kv[1]}</p>
</div>
);
})}
As expected, the content of mapObj is displayed:
This goes against this answer which states that React can't render the result returned from an iterator. This ("test1"):
<h2 className="text-red-800">
using <code>Array.from(map.entries())</code>
</h2>
<CollapsibleFilter title="test1">
{Array.from(mapObj?.entries() ?? []).map((kv) => {
return (
<div className="text-red-400">
<p>name:{kv[0]}</p>
<p>tag:{kv[1]}</p>
</div>
);
})}
</CollapsibleFilter>
also works as expected. The content of mapObj is inside my CollapsibleFilter component, click to open the collapsible:
However if I write ("test2"):
<h2 className="text-blue-800">
using <code>map.entries()</code>
</h2>
<CollapsibleFilter title="test2">
{mapObj?.entries().map((kv) => {
return (
<div className="text-blue-300">
<p>name:{kv[0]}</p>
<p>tag:{kv[1]}</p>
</div>
);
})}
</CollapsibleFilter>
against expectation, when I click the collapsible there is nothing:
Why does "test1" work but "test2" fail?





<CollapsibleFilter>and render it directly in the page, it works."? The image you have included is unreadable: what is it supposed to show? Note that the "reproduction" should not be behind a link. Can you edit your question to show what code you have used to do it "outside of"?<CollapsibleFilter>and render it directly in the page, it works." i was trying to say when passing the object returned by{mapObj?.entries().map(...)}as children to<CollapsibleFilter>component it couldn't render the content, but if i put the exact same{mapObj?.entries().map(...)}outside of<CollapsibleFilter>it renders ok. The image is what theHomecomponent looks in the browser, thename:John tag:abcshould have shown up between "test2" and "outside of collapsible filter" but it didn't<div>inHome, after the last<h2>element, on the same level as the<CollapsibleFilter>element. That's what they mean by "outside" - it's not passed as children toCollapsibleFilteritself. It's pretty clear from even being titled by<h2>outside of collapsible filter</h2>imo.