I am trying to render React Components based on a JSON response from a CMS system, but I can't seem to render the child item components. Can anyone tell me what I am missing/not understanding here?
Here is my code...
// ComponentRenderer.tsx
export const ComponentsHandler: { [key: string]: any } = {
test: Test,
};
export default function ComponentRenderer(config: IBlock): JSX.Element {
if (typeof ComponentsHandler[config.component] !== 'undefined') {
return React.createElement(
ComponentsHandler[config.component],
{
key: config.id,
...config,
},
config.content &&
(typeof config.content === 'string'
? config.content
: config.content.map((c: IBlock) => ComponentRenderer(c)))
);
}
return React.createElement(() => (
<div>
Error: Unable to load <strong>{config.component}</strong> component.
</div>
));
}
Here is the JSON object I am trying to render:
blocks: [
{
id: '1',
component: BlockType.Test,
content: [],
},
{
id: '2',
component: BlockType.Test,
content: [
{
id: '3',
component: BlockType.Test,
content: [],
},
{
id: '4',
component: BlockType.Test,
content: 'this is a string',
},
],
}
]
export interface IBlock {
id: string;
component: BlockType;
content: IBlock[] | string;
}
export enum BlockType {
Test = 'test',
Hero = 'hero',
Video = 'video',
Menu = 'menu',
Navigation = 'navigation',
Testimonial = 'testimonial',
Card = 'card',
CarTile = 'carTile',
HorizontalScrollSection = 'horizontalScrollSection',
Div = 'div',
Section = 'section',
}
Here is the "Test" component I am looking to render in the JSON response
// TestComponent.tsx
export default function Test(props: IBlock) {
return props.content && <p>{props.id}</p>;
}
// UI HomePage
export default function HomePage() {
return (
mockData &&
mockData.blocks.map((block: IBlock) => (
<ComponentRenderer {...block} key={block.id}></ComponentRenderer>
))
);
}
I would expect the browser to render
1 2 3 4
But instead, I only get the top-level JSON items.
1 2
CodePudding user response:
You created a really cool react recursive structure. A few days ago I tried to do something similar but gave up... I will use yours as an inspiration.
Check if this changes can help you:
IBlock:
export interface IBlock {
id: string;
component: BlockType;
content: IBlock[] | string;
children?: React.ReactNode;
}
TestComponent.tsx
export default function Test(props: IBlock) {
return (
props.content && (
<p>
{props.id} {props.children}
</p>
)
);
}