React - map using multiple arrays - javascript

I'm new to React, sorry if this is too basic.
I have an array:
const previews = this.state.previews;
structured like so:
previews: [
"https://p.scdn.co/mp3-preview/86319eaef2091a7f89e53f133ad8d76025e50c4b?cid=d3b2f7a12362468daa393cf457185973",
"https://p.scdn.co/mp3-preview/e5a194941e4268f3861439567b1b5700b6060732?cid=d3b2f7a12362468daa393cf457185973",
]
which I'm mapping in order to render each one of its values, like so:
return (
<div id="parent">
{previews.map((preview, index) =>
<span key={index}>
<Media>
<div className="media">
<div className="media-player">
<Player src={preview} />
</div>
<div className="media-controls">
<PlayPause />
<CurrentTime />
<Progress />
<Duration />
<MuteUnmute />
</div>
</div>
</Media>
</span>)
}
</div>
);
The above works. But lets say I have another array (of corresponding artists for each preview):
const artists = this.state.artists;
structured like so:
artists: [
"The Milk Carton Kids",
"Yo La Tengo",
]
and I want to include this array in the mapping, in order to render each artist as well, like so:
return (
(...)
<td class="number">{ artist }</td>
(...)
);
How do map this second array using map() in the code above?
Do I have to create another object with nested arrays?
What is the best way to go about this?

One approach would be to use the second index parameter passed to your map callback, to access the item of the previews array that corresponds to the current artist item being mapped as follows:
render() {
const { artists, previews } = this.state;
return (<>
{
artists.map((artist, index) => {
/*
Obtain preview from state.previews for current artist index
*/
const preview = previews[index];
/*
Render current artist data, and corresponding preview data
for the current artist
*/
return (<span key={index}>
<span class="number">{ artist }</span>
<Media>
<div className="media">
<div className="media-player">
<Player src={preview} />
</div>
<div className="media-controls">
<PlayPause />
<CurrentTime />
<Progress />
<Duration />
<MuteUnmute />
</div>
</div>
</Media>
</span>)
})
}
</>)
}

Related

Passing data between child components in React

My goal is to use an onClick function in one of my components, and pass that data to another component (end goal is that in the other component called Playlist, it updates an array with the id of the clicked item).
I am just not sure how to pass the information between child components
My main component (app.jsx) looks like this
const mainCards = Data.map(card => {
return(
<MainCard
key={card.id}
id={card.id}
image={card.url}
title={card.title}
playbutton={card.playbutton}
addbutton={card.addbutton}
/>
)
})
const sideCards = SideData.map(card => {
return(
<SideCard
image={card.sideurl}
key={card.id}
title={card.sidetitle}
playbutton={card.playbutton}
addbutton={card.addbutton}
/>
)
})
return (
<div className="App">
<Navbar />
<Header />
<Playlist />
<CardContainer />
<div className="maincards">
{mainCards}
</div>
<div className="sidecards">
{sideCards}
</div>
</div>
)
}
export default App
The component where I am using onClick (MainCard.jsx)
const handleAdd = (id) => {
console.log(id)
}
return(
<div className="mainCardObject">
<div className="cardObj">
<img src={props.image} className ="mainCardImage"/>
<img src={props.playbutton} className="playbutton"/>
<img src={props.addbutton} onClick={() => handleAdd(props.id)} className="addbutton" />
</div>
</div>
)
}
export default MainCard
and the component I wish to pass information to (nothing inside, as I dont know where to start)
return(
<div className="playlistContainer">
<ul>
<li></li>
</ul>
</div>
)
}
export default Playlist```
My suggestion is that you manage 'ids array' state globally creating a context, using the hook useContext(). Here a link with a simple explanation. I hope it helps!
https://www.w3schools.com/react/react_usecontext.asp

Handle an aray of inputs in react

Im trying to handle an aray of inputs in react which are viewed by ".map" function.
The problem is I couldn't give the field a specific value. As a result i couldn't handle the input in onChange function.
I have list of card each card will have a Admin description input and two buttons each one will send a different request.
Here A made the function that present the cards. The porplem is with the input
function createrequestcard(prop){
return(
<Card className="text-center" key={prop._id}>
<div class="wrapper">
<div id="requestspart1" class="left">
<Card.Body>
<Card.Title>Admin Description</Card.Title>
<Card.Text>
<textarea
// --> Value of the index in aray
// --> Handle Change of input
/>
</Card.Text>
</Card.Body>
</div>
<div id="requestspart3" class="left">
<Card.Body>
<Card.Title>CREATE</Card.Title>
<Button variant="outline-success" className="AdminRequestButton">APPROVE</Button>
<Button variant="outline-danger" className="AdminRequestButton">DENY</Button>
</Card.Body>
</div>
</div>
</Card>
)
}
In initialising values on class
this.state = {
requests: [],
description: '',
}
}
The request aray is updated from the backend:
componentDidMount(){
this.checkloginstatus();
axios.get('http://localhost:3000/request', {withCredentials: true})
.then(resp => {
this.setState({requests: resp.data})
}).catch(err => console.log(err))
}
And in render function:
<div>
{this.state.requests.map(createrequestcard)}
</div>
Thank you very much for helping me out.
You can pass index in map method like the below,
<div>
{this.state.requests.map((req,index) => createrequestcard(req, inex))}
</div>
function createrequestcard(prop, index){
The structure of map method is like below
map((element) => { ... } )
map((element, index) => { ... } )
map((element, index, array) => { ... } )
your card component should defined as follow, here i have named it RequestCard (just to make more readable),
this component will get handleOnChange as argument , we will pass updated value to it,when onchange event occurs in
textarea.
function RequestCard(props){
return(
<Card className="text-center" key={prop._id}>
<div class="wrapper">
<div id={props.id} class="left">
<Card.Body>
<Card.Title>Admin Description</Card.Title>
<Card.Text>
<textarea
onChange={(e)=>props.handleOnChange(e.target.value)}
// --> Value of the index in aray
// --> Handle Change of input
/>
</Card.Text>
</Card.Body>
</div>
<div id="requestspart3" class="left">
<Card.Body>
<Card.Title>CREATE</Card.Title>
<Button variant="outline-success" className="AdminRequestButton">APPROVE</Button>
<Button variant="outline-danger" className="AdminRequestButton">DENY</Button>
</Card.Body>
</div>
</div>
</Card>
)}
now you should render it as follow
<div>
{this.state.requests.map((request,index)=>{
return <RequestCard id={index} handleOnChange={(updatedValue)=>this.handleOnChange(index,updatedValue)}
})}
finally your handleOnChange of parent component should be like this,
handleOnChange=(index,updatedValue)=>{
let curState=this.state;
curState.requests[index].value=updatedValue;//note that i am not aware what member of request you want to update
//so i assume value is the member of request object,you can change whatever you want
this.setState(curState);
}

React; Why does my array.map method render only one element from an array?

I am trying to render a component multiple times based on an array passed on from the parent component. But the child component gets whole array as property so does not render correctly. Can I get any tips on that?
App.js
function App() {
const [businesses, setBusinesses] = useState([]);
function searchYelp(term, location) {
Yelp.search(term, location)
.then((businessesList) => {
setBusinesses([businessesList]); // this should be updated to setBusinesses(businessesList)
})
.then(console.log(businesses));
}
console.log(businesses);
return (
<div>
<Hero>
<SearchBar searchYelp={searchYelp} />
</Hero>
<Layout>
<BusinessList businesses={businesses} />
</Layout>
</div>
);
}
BusinessList.js
const BusinessList = ({ businesses }) => {
return (
<div>
<h2 className="business-list__header">İşletmeler</h2>
<div className="business-list">
{businesses.map((business) => {
return <Business business={business} key={business.id} />;
})}
</div>
</div>
);
};
Business.js
const Business = ({ business }) => {
return (
<div className="business">
<div className="business__image-container">
<img src={business.imageSrc} alt="İşletme Görseli" />
</div>
<div className="business__information">
<h2 className="business__header">{business.name}</h2>
<div className="business__address">
<p className="business__address-text">{business.address}</p>
<p className="business__address-text">{business.city}</p>
</div>
<div className="business__reviews">
<h3 className="business__reviews-header">{business.category}</h3>
<h3 className="business__rating">{business.rating}</h3>
</div>
</div>
</div>
);
};
Any tips would be greatly appreciated.
You can see the values components get below.
Child Component
Extended Parent Component

Programmatically adding components with React

I'm fairly new to React and I'm trying to add new components to already exsisting, but I am not sure how to do that.
So here I have the list of my Seances and the button to add more:
SeanceManager.js
return (
<MDBRow>
<MDBCol md="6">
<MDBCard>
<MDBCardBody>
<form>
<p className="h4 text-center py-4">Sign up</p>
<div className="grey-text">
<MDBInput
label="Type your email"
icon="envelope"
group
type="email"
validate
error="wrong"
success="right"
/>
<MDBInput
label="Type your password"
icon="lock"
group
type="password"
validate
/>
</div>
<div className="text-center py-4 mt-3">
<MDBBtn color="cyan" type="submit" onClick={props.submitLogin}>
Log in
</MDBBtn>
</div>
</form>
</MDBCardBody>
</MDBCard>
</MDBCol>
</MDBRow>
);
By pressing the button to add more, a modal view pops up and at the end it has a submit button that should add the Seance.
AddSeanceModal.js
return (
<Modal
{...this.props}
size="lg"
aria-labelledby="contained-modal-title-vcenter"
centered
>
<Modal.Header closeButton>
<Modal.Title id="contained-modal-title-vcenter">Add Seance</Modal.Title>
</Modal.Header>
<Modal.Body>
<div>
<form>
{/*First row*/}
<MDBRow>
<MDBCol md="4">
<div className="custom-file">
<input
type="file"
className="custom-file-input"
id="inputGroupFile01"
aria-describedby="inputGroupFileAddon01"
/>
<label className="custom-file-label" htmlFor="inputGroupFile01">
Choose file
</label>
</div>
</MDBCol>
</MDBRow>
{/*Second row*/}
<MDBRow>
<MDBCol md="4">
<MDBInput
onChange={this.changeHandler}
type="text"
id="materialFormRegisterPasswordEx4"
name="algus_aeg"
label="Algus Aeg"
required
/>
</MDBCol>
<MDBCol md="4">
<MDBInput
onChange={this.changeHandler}
type="text"
id="materialFormRegisterPasswordEx4"
name="lopp_aeg"
label="Lõpp Aeg"
required
/>
</MDBCol>
</MDBRow>
{/*Third row*/}
<MDBRow>
<MDBCol md="4">
<MDBInput
onChange={this.changeHandler}
type="text"
id="materialFormRegisterPasswordEx4"
name="aja_samm"
label="Aja Samm"
required
/>
</MDBCol>
</MDBRow>
<Button variant="secondary" onClick={this.props.onHide}>
Close
</Button>
<MDBBtn color="success" type="submit" className="float-right">
Add Seance
</MDBBtn>
</form>
</div>
</Modal.Body>
</Modal>
);
And finally the Seance itself:
Seance.js
return (
<div
className="card"
style={{ marginBottom: "7px" }}
onClick={() => this.setState({ modalShow: true })}
>
<div className="card-body">
<h5 className="card-title">Seance nr: {this.props.id}</h5>
<p className="card-text">Start aeg: {this.props.startDate}</p>
<p className="card-text">End aeg: {this.props.endDate}</p>
<button type="button" className="close float-right" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
<ResultModal id={1} show={this.state.modalShow} onHide={modalClose} />
</div>
</div>
);
I also made a fiddle on sandbox:
https://codesandbox.io/s/qloo1vqr7j?fontsize=14
At the moment I have 4 static Seances, but it should start with 0 and add more once you add them.
Also the Xon the Seance should remove it.
I have tried creating a list on state in SeanceManager.js, but I have not understoond how to add components to the list from another component AddSeanceModal.
There are quite a few code choices that are holding your application back from being dynamic.
First, you'll need to utilize a container component that handles all things related to Seance state. This includes, adding, removing and viewing. All of this needs to be handled by a parent that updates its children according to what current in its state. Please follow this guide to understand containers vs components.
Second, you need to refactor how you're utilizing Seances. They need to be stored as an array within the parent's state. As you have it now, it's hard-coded with 4 <Seances /> that can't be removed/updated.
Instead, you should create an array of objects like so within state:
seances: [
{ id: "1", startDate: "2019-04-10 10:28:05.926-07", endDate: "2019-05-10 10:28:05.924-07" },
{ id: "2", startDate: "2019-04-11 11:28:05.926-07", endDate: "2019-05-11 11:28:05.924-07" },
{ id: "3", startDate: "2019-04-12 12:28:05.926-07", endDate: "2019-05-12 12:28:05.924-07" },
...etc
];
You'll utilize Array.map to map over this seances array and implicitly return these seances dynamically (to make easier to read, I'm writing your <Seance id={id} /> component inline):
<div>
{seances.map(({ id, startDate, endDate }) => (
<div key={id}>
<h1>Seance nr: {id} </h1>
<p>Start date: {startDate}</p>
<p>End date: {endDate}</p>
<button type="button" onClick={() => handleDeleteSeance(id)}>X</button>
</div>
))}
</div>
Now you'll remove these items from the seances array by utilizing Array.filter by a seance's id property. When a user clicks the "X" button it calls this.handleDeleteSeance with the specific seance id:
handleDeleteSeance = id => {
this.setState(prevState => ({
...prevState, // spread out any previous state not related to "seances"
seances: prevState.seances.filter(seance => seance.id !== id) // this translates to: compare each "seance" within "seances" and implicitly return any that do NOT match the "id" that was clicked
})
};
To add an item to the seances array, you'll utilize the spread operator, and add an object with properties that were collected from a form:
handleSubmit = e => {
e.preventDefault();
const { id, startDate, endDate } = this.state;
if (!id || !startDate || !endDate } return null; // if any properties are missing, don't add an item yet
this.setState(prevState => ({
...prevState, // spread out any previous state not related to "seances"
seances: [...prevState.seances, { id, startDate, endDate } ] // this translates to: spread out any previous seances and add a new object with "id", "startDate" and "endDate"
});
}
Working with arrays example (since this a client-only implementation, I decided to filter by array position so I don't have to have to deal with generating unique ids; however, you can utilize uuid to generate these dynamic ids if you wish):
Other notes:
One of your components contains a let modalClose = () =>
this.setState({ modalShow: false }); within the render method.
This is invalid and should be defined as a class method.
Structure your components to be reusable. You have some components that are, but then you have some that reside within another component (like FileInput). As your application grows, you may need to use FileInput elsewhere for another form.
Utilize ES6 syntax throughout your project to simplify your code. For example you can deconstruct props for your Login component like so: const Login = ({ submitLogin }) => ( <MDBRow>...etc</MDBRow>);.

How to traverse key-value in reactjs

I'm trying to efficiently make a key-value tour in an array in reactjs.
However, I do not know how to display the "key" array without creating another map function.
const equipos = [
{
key: [
'Real Madrid',
'Manchester United',
'Champions League',
'English Premier League',
'Bayern Munchen',
'Juventus',
'Arsenal',
],
images: [
'http://as00.epimg.net/img/comunes/fotos/fichas/equipos/large/1.png',
'https://4.bp.blogspot.com/_Z_YWTFNHUvw/TQDBKNXdwcI/AAAAAAAACI0/COhwIG2PkFA/s320/Manchester-United%255B1%255D.png',
'http://www.stickpng.com/assets/images/5842fe06a6515b1e0ad75b3b.png',
'https://i.pinimg.com/originals/f1/44/fc/f144fc61d0b0ed7716d740aa9deefb07.png',
'https://upload.wikimedia.org/wikipedia/commons/thumb/1/1b/FC_Bayern_M%C3%BCnchen_logo_%282017%29.svg/1200px-FC_Bayern_M%C3%BCnchen_logo_%282017%29.svg.png',
'http://as00.epimg.net/img/comunes/fotos/fichas/equipos/large/29.png',
'https://i.pinimg.com/originals/98/32/8b/98328b1d64c5f93ef5eceea7586430d1.png',
],
},
];
console.log(equipos.images)
const IconosSeccion = () => (
<div className="containerIconosSeccion">
<div className="parentIconos" />
{equipos.map(equipo => (
<div>
{equipo.images.map(image => (
<div className="backgroundIconoIndependiente">
<img alt="#" className="iconoIndependiente" src={image} />
<span className="textoIconoIndependiente">Real madrid</span>
</div>
))}
</div>
))}
</div>
);
I want to show it in the span of the classname textIconoIndependiente.
Thank you!
I'm not sure your data is well formatted.
I suggest you another format. In that way, It's possible to render your image and the name of the club with only one map.
const equipos = [
{name:'Real Madrid',image:'http://as00.epimg.net/img/comunes/fotos/fichas/equipos/large/1.png',},
{name:'Manchester United',image:'https://4.bp.blogspot.com/_Z_YWTFNHUvw/TQDBKNXdwcI/AAAAAAAACI0/COhwIG2PkFA/s320/Manchester-United%255B1%255D.png',},
{name:'Champions League',image:'http://www.stickpng.com/assets/images/5842fe06a6515b1e0ad75b3b.png',},
{name:'English Premier League',image:'https://i.pinimg.com/originals/f1/44/fc/f144fc61d0b0ed7716d740aa9deefb07.png',},
{name:'Bayern Munchen',image:'https://upload.wikimedia.org/wikipedia/commons/thumb/1/1b/FC_Bayern_M%C3%BCnchen_logo_%282017%29.svg/1200px-FC_Bayern_M%C3%BCnchen_logo_%282017%29.svg.png',},
{name:'Juventus',image:'http://as00.epimg.net/img/comunes/fotos/fichas/equipos/large/29.png',},
{name:'Arsenal',image:'https://i.pinimg.com/originals/98/32/8b/98328b1d64c5f93ef5eceea7586430d1.png'}
];
const IconosSeccion = () => (
<div className="containerIconosSeccion">
<div className="parentIconos" />
{equipos.map(equipo => (
<div>
<div className="backgroundIconoIndependiente">
<img alt="#" className="iconoIndependiente" src={equipo.image} />
<span className="textoIconoIndependiente">{equipo.name}</span>
</div>
</div>
))}
</div>
);
The second argument passed to the map function is the index, which you could use to read the correct key.
const IconosSeccion = () => (
<div className="containerIconosSeccion">
<div className="parentIconos" />
{equipos.map(equipo => (
<div>
{equipo.images.map((image, index) => (
<div className="backgroundIconoIndependiente">
<img alt="#" className="iconoIndependiente" src={image} />
<span className="textoIconoIndependiente">{equipo.key[index]}</span>
</div>
))}
</div>
))}
</div>
);
Use the second parameter to map, index, to get the correct image and key.
const IconosSeccion = () => (
<div className="containerIconosSeccion">
<div className="parentIconos" />
{equipos.map((equipo, i) => (
<div>
<div className="backgroundIconoIndependiente">
<img alt="#" className="iconoIndependiente" src={ equipo.images[i] } />
<span className="textoIconoIndependiente">{ equipo.key[i] }</span>
</div>
</div>
)}
</div>
);

Categories