everyone. I'm fairly new to React Js, and I got stuck while trying to render images from an array. My code is :
import { home } from "../homeObj";
const Logos = ({ title, img, img2, img3, key }) => {
return (
<>
<div className={styles.anaShif} key={key}>
<h2 className={styles.h2}> {title} </h2>
<div className={styles.logos}>
<img
id={img.id}
src={img}
alt={img.alt}
className={styles.img}
srcSet={`${img2} 2x,
${img3} 3x`}
/>
</div>
</div>
</>
);
};
function Trusted() {
const logoComponent = home.map((_objects, i, id) => {
if (home[i].id === "logos") {
return (
<>
<Logos
key={i}
id={id}
title={home[i].title}
img={home[i].logos[i].src}
/>
</>
);
}
});
return (
<>{logoComponent}</>
);
}
export default Trusted;
and { home } :
export const home = [
{
id: "logos",
title: "Welcome to",
logos: [
{
id: 1,
alt: "car",
src: require("./logo_06.png"),
src2: require("./logo_06#2x.png"),
src3: require("./logo_06#2x.png"),
},
{
id: 2,
alt: "red-car",
src: require("./logo_07.png"),
src2: require("./logo_07#2x.png"),
src3: require("./Tlogo_07#3x.png"),
},
],
]
What happens is that I can only display an image of the 2nd element of logos array, it's like React Js completely skips 1st element(id, img.src, alt).
What I want to do is to be able to display both images at the same time and also add elements dynamically, when a new element gets added to {home}, it should display without hardcoding.
Any help would be greatly appreciated.
You have to return the map of logos which is not happening in your current code. Hence you are getting only one Logos component.
import React from 'react';
import { home } from '../homeObj';
const Logos = ({ title, img, img2, img3, key }) => {
return (
<>
<div className={styles.anaShif} key={key}>
<h2 className={styles.h2}> {title} </h2>
<div className={styles.logos}>
<img
id={img.id}
src={img}
alt={img.alt}
className={styles.img}
srcSet={`${img2} 2x,
${img3} 3x`}
/>
</div>
</div>
</>
);
};
function Trusted() {
const logosIndex = home.findIndex((obj) => obj.id === 'logos');
const logos = home[logosIndex].logos.map(({ id, alt, src }) => {
return <Logos key={id} id={id} title={home[logosIndex].title} img={src} />;
});
return logos;
}
export default Trusted;
You not only need to map over home but also the logos array with-in each home object. Also in your case, you don't need the third arg which is the entire array.
Simplify your code like this:
home.map((_objects, i) => {
if (_objects.id === "logos") {
return (
<>
{
_objects.logos.map(logo => (
<Logos
key={logo.id}
id={logo.id}
title={logo.title}
img={logo.src}
/>
))
}
</>
);
}else {
return null
}
}
Without much if else, you can also write like this.
home.filter((_objects, i) => _objects.id === 'logos')
.map(({logos})=>logos.map(logo=>
<Logos
key={logo.id}
{...logo}
/>))
Related
I've got a set of dashboards that display in bootstrap cards on a front page and I would like to wrap them in a div with the class row for every 3rd entry. I was thinking about marking my dashboard component with the DB id from props and use a modulus function, but that will cause problems if an ID is deleted
Dashboard component:
export type DashboardProps = {
id: number
title: string
description: string
}
const Dashboard: React.FC<{ dashboard: DashboardProps }> = ({ dashboard }) => {
return (
<>
<div className="col-sm-12 col-lg-4">
<div className="card bg-light h-100">
<div className="card-header">
{dashboard.title}
</div>
<div className="card-body d-flex flex-column">
<p className="card-text">
{dashboard.description}
</p>
<a className="btn btn-info text-center mt-auto"
onClick={() =>
Router.push("/dashboard/[id]", `/dashboard/${dashboard.id}`)
}
>Go to dashboard</a>
</div>
</div>
</div>
</>
)
}
export default Dashboard
Index page:
type Props = {
dashboards: DashboardProps[]
}
export const getServerSideProps: GetServerSideProps = async () => {
const dashboards = await prisma.dashboard.findMany({
orderBy: { id: "asc", },
})
return {
props: JSON.parse(JSON.stringify({ dashboards })),
}
}
const Index: React.FC<Props> = (props) => {
const { data: session, status } = useSession()
if (status === "loading") {
return (
<Spinner />
)
}
if (!session) {
return (
<Layout>
<AccessDenied />
</Layout>
)
}
return (
<Layout>
<h1>Dashboards</h1>
{props.dashboards.map((dashboard) => (
<Dashboard dashboard={dashboard} />
))}
</Layout>
)
}
export default Index
I could also potentially wrap them in a single div with class row, but I would need to enforce a top/bottom margin so the cards don't stack right on top of each other. Any tips to get me rolling on this would be greatly appreciated!
.map provides index, you this to find every 3rd element.
//...
{
props.dashboards.map((dashboard, index) =>
(index + 1) % 3 === 0 ? (
<div>
<Dashboard key={dashboard.id} dashboard={dashboard} />
</div>
) : (
<Dashboard key={dashboard.id} dashboard={dashboard} />
)
)
}
Using React, I have created a component named ItemPoke.
Then I use array.map to read my data from a small array of 4 objects.
The structure is dasplay but not the images and the content of h3 and span labels. Using the dev tool from Chrome I can see the props value, the strigs are correctly asignated, but the image and info are not displayed.
My code:
const pokemons = [
{
name: "bulbasaur",
url: "https://pokeapi.co/api/v2/pokemon/1/",
sprite:
"https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/1.png",
type: "water"
},
{
name: "ivysaur",
url: "https://pokeapi.co/api/v2/pokemon/2/",
sprite:
"https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/2.png",
type: "water"
},
{
name: "venusaur",
url: "https://pokeapi.co/api/v2/pokemon/3/",
sprite:
"https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/3.png",
type: "water"
},
{
name: "Charmander",
url: "https://pokeapi.co/api/v2/pokemon/3/",
sprite:
"https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/6.png",
type: "fire"
}
];
export default pokemons;
import '../App.css';
function ItemPoke({NamePoke,TypePoke,ImagePoke}) {
return (
<article className='list-pokemons-item'>
<div className='list-pokemons-item-content'>
<img
src={ImagePoke}
alt="pokemon-1"
></img>
<h3>{NamePoke}
<span>{TypePoke}</span>
</h3>
</div>
</article>
);
}
export default ItemPoke;
import '../App.css';
import ItemPoke from '../components/ItemPoke';
import pokemons from '../components/data';
export default function ListPoke() {
const ListaPokemons = pokemons.map ((item, index) => {
return(
<ItemPoke key = {index} name= {item.name} type = {item.type} image = {item.sprite} />
)
}
)
return (
<main className="list-pokemons">
{ListaPokemons}
</main>
);
}
You need to match the prop names you passing to your component:
function ItemPoke({ name, type, image }) {
return (
<article className="list-pokemons-item">
<div className="list-pokemons-item-content">
<img src={image} alt="pokemon-1"></img>
<h3>
{name}
<span>{type}</span>
</h3>
</div>
</article>
);
}
The following is a simplified version of the part of the entire code.
The entire app is basically supposed to be a note taking app built on React, and currently I'm stuck on its respective note's editing function.
So the following script part basically is supported to do:
Render an array of <Card /> components based on the App this.state.notes array
By clicking the Edit note button it sets the App this.state.noteEditingId state
(so the React instance can know later which generated Card is currnetly being edited by the id)
By clicking the Save Edit button it tries to update the App this.state.notes array with the submitted edit text.
(see, I used a lot of filters to try to achieve this, since I don't have a good idea to how to more nicely achieve this. I believe there should be a nicer way)
But the result is not what I expect.
(While it supposed to achieve updating the expected Card component's notes array with the new note instance's new note "note" text,
it updates the notes array with the different notes's note instance's "note" text. I cannot explain this clearly, since this is an idk-what-is-wrong type of issue to me. )
const Card = (props) => {
const [noteEditing, setNoteEditing] = useState(false);
return (
<div {...props}>
<div>
<div>
<span>
<button onClick={() => {
noteEditing ? setNoteEditing(false) : setNoteEditing(true);
props.thisApp.setState({ noteEditingId: props.config.id })
}}>Edit note</button>
</span>
{noteEditing
?
<div>
<textarea className='__text' />
<button onClick={() => {
let note = document.querySelector('.__text').value
let current_note = props.thisApp.state.notes.filter(a => a.id == props.config.id)[0]
let notesAfterRemoved = props.thisApp.state.notes.filter(a => a.id !== props.config.id)
if (props.thisApp.state.noteEditingId == props.config.id)
{
props.thisApp.setState({
notes: [...notesAfterRemoved, { ...current_note, note: note }]
})
}
}}>
Save Edit
</button>
</div>
: ""
}
</div>
</div>
</div>
)
}
class App extends React.Component {
constructor() {
super();
this.state = {
notes: [
{
note: "note1.",
id: nanoid(),
},
{
note: "note2.",
id: nanoid(),
},
{
note: "note3.",
id: nanoid(),
},
]
};
}
render() {
return (
<div>
<h2>
Notes ({this.state.notes.length})
</h2>
<div className='__main_cards'>
<div>
{this.state.notes.map((a, i) => {
return <Card key={i} className="__card" thisApp={this} config={
{
note: a.note,
}
} />
})}
</div>
</div>
</div>
)
}
}
So what can I do fix to make the part work properly? Thanks.
You should pass the current note also and get rid of the filter in card component:
this.state.notes.map((note, i) => {
return (
<Card
key={i}
className="__card"
thisApp={this}
currentNote={note}
/>
);
})
And then remove this:
let current_note = props.thisApp.state.notes.filter(a => a.id == props.config.id)[0]
And then rather than finding the notes without the edited note you can create a new array with edited data like this:
const x = props.thisApp.state.notes.map((n) => {
if (n.id === props.currentNote.id) {
return {...n, note}
}
return n
})
And get rid off this line:
let notesAfterRemoved = props.thisApp.state.notes.filter(a => a.id !== props.config.id)
Also, change this
noteEditing ? setNoteEditing(false) : setNoteEditing(true);
To:
setNoteEditing(prev => !prev)
As it is much cleaner way to toggle the value.
And I believe, there is no need to check that if the noteEditingId is equal to current active note id.
So you can get rid off that also (Correct me if I am wrong!)
Here's the full code:
const Card = (props) => {
const [noteEditing, setNoteEditing] = useState(false);
const textareaRef = useRef();
return (
<div {...props}>
<div>
<div>
<span>
<button
onClick={() => {
setNoteEditing((prev) => !prev); // Cleaner way to toggle
}}
>
Edit note
</button>
</span>
{noteEditing && (
<div>
<textarea className="__text" ref={textareaRef} />
<button
onClick={() => {
let note = textareaRef.current.value;
const x = props.thisApp.state.notes.map(
(n) => {
if (n.id === props.currentNote.id) {
return { ...n, note };
}
return n;
}
);
props.thisApp.setState({
notes: x,
});
}}
>
Save Edit
</button>
</div>
)}
</div>
</div>
</div>
);
};
class App extends React.Component {
constructor() {
super();
this.state = {
notes: [
{
note: "note1.",
id: nanoid(),
},
{
note: "note2.",
id: nanoid(),
},
{
note: "note3.",
id: nanoid(),
},
],
};
}
render() {
return (
<div>
<h2>Notes ({this.state.notes.length})</h2>
<div className="__main_cards">
<div>
{this.state.notes.map((note, i) => {
return (
<Card
key={i}
className="__card"
thisApp={this}
currentNote={note}
/>
);
})}
</div>
</div>
</div>
);
}
}
}
);
props.thisApp.setState({
notes: x,
});
}
}}
>
Save Edit
</button>
</div>
)}
</div>
</div>
</div>
);
};
class App extends React.Component {
constructor() {
super();
this.state = {
notes: [
{
note: "note1.",
id: nanoid(),
},
{
note: "note2.",
id: nanoid(),
},
{
note: "note3.",
id: nanoid(),
},
],
};
}
render() {
return (
<div>
<h2>Notes ({this.state.notes.length})</h2>
<div className="__main_cards">
<div>
{this.state.notes.map((note, i) => {
return (
<Card
key={i}
className="__card"
thisApp={this}
currentNote={note}
/>
);
})}
</div>
</div>
</div>
);
}
}
I hope this is helpful for you!
I tried to add click handler on my code, the idea is when i click on my first card, the first card add new class "selected" nad when i click on my third card or else the third card or else will add new class "selected". There is a problem when i was clicking on any card it was always first card was selected. Please help me. Thank you.
Parent code
import React, { useState } from 'react';
import CardBus from '../CardBus/CardBus.component';
import './BusSelector.style.css'
function BusSelector() {
const [buses, setBuses] = useState([
{
busNumber: 1,
destination: 'Cibiru - Cicaheum',
stopTime: '09:20 - 09.45',
stopLocation: 'Jl.Jendral Sudirman',
isSelected: false
},
{
busNumber: 2,
destination: 'Cicaheum - Cibereum',
stopTime: '09:10 - 09.20',
stopLocation: 'Jl.Soekarno Hatta',
isSelected: false
},
]);
return (
<div className="bus-selector--container">
{buses.map((bus) => {
return <CardBus key={bus.busNumber} eachBus={bus} buses={buses} setBuses={setBuses} />
})}
</div>
);
}
export default BusSelector;
Child code:
import React from 'react';
import './CardBus.style.css';
import TimeProgressThin from '../../icon/Time_progress_thin.svg';
import PinLight from '../../icon/Pin_light_thin.svg';
function CardBus(props) {
const [isSelected, setIsSelected] = useState(false)
let { eachBus, buses, setBuses} = props;
const selectedHandler = () => {
if (isSelected) {
const card = document.querySelector('.card');
card.classList.add('selected');
return setIsSelected(!isSelected);
}
else {
const card = document.querySelector('.card');
card.classList.remove('selected');
return setIsSelected(!isSelected);
}
}
return (
<div key={eachBus.key} className="card" onClick={selectedHandler}>
<div className="bus--left">
<h1>{eachBus.busNumber}</h1>
</div>
<div className="bus--right">
<div className="title">
<h1>{`Armada ${eachBus.busNumber}`}</h1>
<h2>{eachBus.destination}</h2>
</div>
<div className="detail">
<div className="detail--item">
<div>
<img src={TimeProgressThin} alt="Time Progress Logo" />
</div>
<div className="detail_content">
<h3>Last stopped</h3>
<h3>{eachBus.stopTime}</h3>
</div>
</div>
<div className="detail--item">
<div>
<img src={PinLight} alt="Pin Light Logo" />
</div>
<div className="detail_content">
<h3>Location Stopped</h3>
<h3>{eachBus.stopLocation}</h3>
</div>
</div>
</div>
</div>
</div>
);
}
export default CardBus;
Allow multiple selections
function CardBus(props) {
const [isSelected, setIsSelected] = useState(false);
let { eachBus, buses, setBuses } = props;
return (
<div key={eachBus.key} className={`card ${isSelected ? 'selected' : ''}`} onClick={() => setIsSelected(!isSelected)}>
...
</div>
);
}
export default CardBus;
Allow single select
You can simplify the code a lot if you move the selected child logic to the parent.
Parent code:
function BusSelector() {
const [buses, setBuses] = useState([
{
busNumber: 1,
destination: 'Cibiru - Cicaheum',
stopTime: '09:20 - 09.45',
stopLocation: 'Jl.Jendral Sudirman',
isSelected: false
},
{
busNumber: 2,
destination: 'Cicaheum - Cibereum',
stopTime: '09:10 - 09.20',
stopLocation: 'Jl.Soekarno Hatta',
isSelected: false
},
]);
const [selectedBus, setSelectedBus] = useState(-1);
return (
<div className="bus-selector--container">
{buses.map((bus) => {
return <CardBus
key={bus.busNumber}
eachBus={bus}
buses={buses}
setBuses={setBuses}
onClick={() => setSelectedBus(bus.busNumber)}
isSelected={bus.busNumber === selectedBus} />;
})}
</div>
);
}
export default BusSelector;
Child code:
function CardBus(props) {
let { eachBus, isSelected, buses, setBuses, onClick } = props;
return (
<div key={eachBus.key} className={`card ${isSelected ? 'selected' : ''}`} onClick={onClick}>
...
</div>
);
}
export default CardBus;
Intro -
I'm trying to show JSON data in a Accordion. So I used react-sanfona (github) to build that. I'm trying to call getComponent function recursively so that I can check if it is an array or object if it is array I'm calling same function. better show you the pogress so far.
Problem - I'm getting [object Object] at the second level even I call the getComponent recursively
Edit on codesandbox
Problem was that you didn't return anything when dealing with object
So this part
Object.keys(record).map((key, index) => {
console.log(44);
return (
<AccordionItem className="ml-5" title={`1 - ${index}`} expanded>
{getComponent(record[key])}
</AccordionItem>
);
});
should be
return Object.keys(record).map((key, index) => {
console.log(44);
return (
<AccordionItem className="ml-5" title={`1 - ${index}`} expanded>
{getComponent(record[key])}
</AccordionItem>
);
});
I added a default expanded property and now it displays all data.
Check
this sandbox
Hy, I don't know exactly what you want to display but here is a version working.
import { Accordion, AccordionItem } from "react-sanfona";
import "./styles.css";
const datalist = [
{
id: 3423235234,
name: "John",
address: [
{
first: "city1",
second: "city2"
}
]
}
];
export default function App() {
function getComponent(record) {
if (Array.isArray(record)) {
return record.map((b, index) => (
<AccordionItem className="ml-5" title={`${index}`} key={index}>
{getComponent(b)}
</AccordionItem>
));
}
if (typeof record === "object") {
return (
<div>
{Object.keys(record).map((key, index) => {
return (
<AccordionItem
className="ml-5"
title={`1 - ${index}`}
expanded
key={index}
>
{getComponent(record[key])}
</AccordionItem>
);
})}
</div>
);
}
if (typeof record === "string" || typeof record === "number") {
console.log("string or number: ", record);
return <AccordionItem className="ml-5" title={`2 - ${record}`} />;
}
return (
<AccordionItem className="ml-5" title={`3 - ${record.toString()}`} />
);
}
return (
<div className="App">
<div className="px-7">
<Accordion>{getComponent(datalist)}</Accordion>
</div>
</div>
);
}
The package you're using raise many errors in the console (with no configuration). Did you check the material ui's accordions ? https://material-ui.com/components/accordion/
This is working code, which might help you
Here, initially root folder will be hiding all children once click on root -> its direct children will be displayed/hidden and so on
//explorer is the json coming from parent component
import React, { useState } from "react";
function Accodian({ explorer }) {
const [expand, setExpand] = useState(false);
if (explorer.children) {
return (
<div>
{/* {explorer.children ? ( */}
<>
<div onClick={() => setExpand((prevState) => !prevState)}>
{explorer.name}
</div>
{expand ? (
<>
{explorer.children.map((child) => {
// return <div key={child.name} style = {{paddingLeft: '20px'}}>{child.name}</div>;
return <Accodian explorer={child} key={child.name} />;
})}
</>
) : null}
</>
{/* ) : null} */}
</div>
);
} else {
return <div style={{ paddingLeft: "20px" }}>{explorer.name}</div>;
}
}
export default Accodian;
Check this sandbox : https://codesandbox.io/s/js-recursion-accordian-v03y5q?file=/src/components/Accordian.js:0-823/