I'm learning React (v18) and I've tried implementing some similar solutions that I've found here, but so far no success. I have been trying display "cards" with names and pictures from a list of authors; the url for the each picture is remote (for example from wikipedia):
export const Authors = () => {
const authors = [
{
id: 1,
name: 'Tayeb Salih',
wikipedia: 'https://en.wikipedia.org/wiki/Tayeb_Salih',
picture_url: 'https://upload.wikimedia.org/wikipedia/commons/a/ae/Tayeb_saleh.jpg'
},
{
id: 2,
name: 'Adolfo Bioy Casares',
wikipedia: 'https://en.wikipedia.org/wiki/Adolfo_Bioy_Casares',
picture_url: 'https://upload.wikimedia.org/wikipedia/commons/a/a9/Bioy.png'
},
{
id: 3,
name: 'Lucy Foley',
wikipedia: 'https://en.wikipedia.org/wiki/Lucy_Foley',
picture_url: 'https://www.intrinseca.com.br/upload/autor/Lucy%20Foley%20G.jpg'
},
{
id: 4,
name: 'Matt Ruff',
wikipedia: 'https://en.wikipedia.org/wiki/Matt_Ruff',
picture_url: 'https://images.gr-assets.com/authors/1597587995p5/40577.jpg'
}];
const cards:any[] = [];
authors.forEach((author, index) => {
const borderColor = index > 1 ? null : index > 0 ? '#57b60a' : '#b60a57';
const item = (
<Card borderColor={borderColor} title={author.name} subtitle={author.id.toString()} key={index}>
<img src={require(`${author.picture_url}`)} alt={author.name} />
</Card>
);
cards.push(item);
});
return (
<section id="section-authors">
<h1>Authors</h1>
<div className="group-cards">
{cards}
</div>
<img
src="https://upload.wikimedia.org/wikipedia/commons/a/a9/Bioy.png"
alt=""
height="200"
/>
</section>
);
};
In this way I get the following error: Uncaught Error: Cannot find module 'https://upload.wikimedia.org/wikipedia/commons/a/ae/Tayeb_saleh.jpg'.
When I change the code to <img src={require(author.picture_url)} alt={author.name} />
, the same error occurs.
If I trying use only <img src={author.picture_url} alt={author.name} />
, error: *Uncaught Error: img is a void element tag and must neither have children
nor use dangerouslySetInnerHTM*... then, with a little bit of insanity: ```<img src={
${author.picture_url}`} alt={author.name} />```... same error...
What's wrong anyway?
<Card/>
export const Card = (props: {
title?: string | null | undefined;
subtitle?: string | null | undefined;
borderColor?: string | null | undefined;
children?: any;
}) => {
const cardStyle = {
borderColor: props.borderColor || '#ded3d3',
};
return (
<div className="card-minhoteca" style={cardStyle}>
<CardHeader title={props.title} subtitle={props.subtitle} />
<CardBody>
{
React.Children.map(props.children, (child, i) => {
return React.cloneElement(child, { ...props, key: i });
})
}
</CardBody>
<CardFooter>
<p>Card Footer</p>
</CardFooter>
</div>
);
};
export const CardHeader = (props: { title?: string; subtitle?: string; }) => {
return (
<div className="card-header">
<h3 className="title">{props.title}</h3>
<h4 className="card-subtitle">{props.subtitle}</h4>
</div>
);
};
export const CardBody = (props: {
children?: any
}) => {
return (
<div className="card-body">
{props.children}
</div>
);
};
export const CardFooter = (props: {
children?: any;
callBack?: any;
}) => {
return (
<div className="card-footer">
{props.children}
<button onClick={(e) => {
e.preventDefault();
props.callBack();
}}>Click!</button>
</div>
);
};
UPDATE
You can use {props.children}
in Card
to render the children in the place needed. This should solve the error if you also changed the way of using img
as the original answer.
In this use case, the children
are not modified in their placement, which would not justify the use of React.cloneElement()
over props.children
. Hope this will help.
Example:
// Replaces Card component
export const Card = (props: {
title?: string | null | undefined;
subtitle?: string | null | undefined;
borderColor?: string | null | undefined;
children?: any;
}) => {
const cardStyle = {
borderColor: props.borderColor || '#ded3d3',
};
return (
<div className="card-minhoteca" style={cardStyle}>
<CardHeader title={props.title} subtitle={props.subtitle} />
<CardBody>{props.children}</CardBody>
<CardFooter>
<p>Card Footer</p>
</CardFooter>
</div>
);
};
Original
Instead of using forEach
you can use map()
to map out authors
.
This way there is no need to create another cards
array so component is cleaner.
Regarding the img
, to pass the url it should be <img src={author.pictureUrl} />
, I also changed the property name from picture_url
to pictureUrl
in authors
.
If the issue remains, the error is in Card
component. Maybe {props.children}
was not placed properly, but will need to see the Card
component to find out.
Example:
export const Authors = () => {
const authors = [
{
id: 1,
name: "Tayeb Salih",
wikipedia: "https://en.wikipedia.org/wiki/Tayeb_Salih",
pictureUrl:
"https://upload.wikimedia.org/wikipedia/commons/a/ae/Tayeb_saleh.jpg",
},
{
id: 2,
name: "Adolfo Bioy Casares",
wikipedia: "https://en.wikipedia.org/wiki/Adolfo_Bioy_Casares",
pictureUrl:
"https://upload.wikimedia.org/wikipedia/commons/a/a9/Bioy.png",
},
{
id: 3,
name: "Lucy Foley",
wikipedia: "https://en.wikipedia.org/wiki/Lucy_Foley",
pictureUrl:
"https://www.intrinseca.com.br/upload/autor/Lucy%20Foley%20G.jpg",
},
{
id: 4,
name: "Matt Ruff",
wikipedia: "https://en.wikipedia.org/wiki/Matt_Ruff",
pictureUrl: "https://images.gr-assets.com/authors/1597587995p5/40577.jpg",
},
];
return (
<section id="section-authors">
<h1>Authors</h1>
<div className="group-cards">
{authors.map((author, index) => {
const borderColor =
index > 1 ? null : index > 0 ? "#57b60a" : "#b60a57";
return (
<Card
borderColor={borderColor}
title={author.name}
subtitle={author.id.toString()}
key={index}
>
<img src={author.pictureUrl} alt={author.name} />
</Card>
);
})}
</div>
<img
src="https://upload.wikimedia.org/wikipedia/commons/a/a9/Bioy.png"
alt=""
height="200"
/>
</section>
);
};