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);
}
Related
I want to add some text after the cards have rendered three times. these cards take value from a local json file using split and map. i also want to change the text every three times the component has rendered.
import React from 'react';
import data from '../data.json';
import { Card,Button } from 'react-bootstrap';
function Med() {
return (
<div>
{data.slice(0,6).map((item)=>{
return (
<div>
<Card style={{ width: '18rem' }}>
<Card.Body>
<Card.Title>{item.Name}</Card.Title>
<Card.Text>
This is the explaination of product card.
</Card.Text>
<Button variant="primary">Add to cart</Button>
</Card.Body>
</Card>
</div>
)
})}
</div>
)
}
export default Med;
This is my current output. but I want it to display some text after chris's card.
{data.slice(0,6).map((item,index)=>{//use the index to conditionally render })}
Edit #1 -
So, after reading your comment maybe this code will give you an idea
{data.slice(0, 6).map((item, index) => {
return (
<div>
{index < 3 ? <h1>First three</h1> : <h1>From Fourth</h1>
</div>
);
})}
I know how to pass basic props in React but I am a bit stumped on this one. Rather than try to explain all of it in this paragraph I think showing will do better justice to the problem.
Here is how it was before trying to break it off into its own separate component.
{ <div className="flex-item-main">
<ol>
{this.state.thoughts.map((thought, index)=>
<DisplayPoem className='displayPoem' key={index} onClick={() => { this.handleDeleteClick(index) }} name='Delete Thoughts' value={thought} />
)}
</ol>
</div> }
Here is how it will look as its own separate component taking props from the parent:
{ <div className="flex-item-main">
<ol>
{this.props.thoughtsProp.map((thought, index)=>
<DisplayPoem className='displayPoem' key={index} onClick={this.props.onClick} name={this.props.name} value={thought} />
)}
</ol>
</div> }
Here is the parent component passing down the props: I have no idea what to do with onClick={() => { this.handleDeleteClick(index) }} as I need index from the .map() function in the component. I hope any of this made sense and I am happy to add updates, im not sure how to explain the problem which is probably why im having trouble solving it. Still new to React.
<DisplayPoemList thoughtsProp={this.state.thoughts} onClick={() => { this.handleDeleteClick(index) }} name='Delete Thoughts' />
You could remove the arrow function from props and pass you method as a prop.
Change this code (look at the onClick):
<DisplayPoemList thoughtsProp={this.state.thoughts} onClick={() => { this.handleDeleteClick(index) }} name='Delete Thoughts' />
To this code:
<DisplayPoemList thoughtsProp={this.state.thoughts} onClick={this.handleDeleteClick } name='Delete Thoughts' />
Then your DisplayPoemList will get a function onClick that expecting for index when it called.
Use an arrow function to create an instance of the function with an index in a scope for each element.
{ <div className="flex-item-main">
<ol>
{this.props.thoughtsProp.map((thought, index)=>
<DisplayPoem className='displayPoem' key={index} onClick={() => this.props.onClick(index)} name={this.props.name} value={thought} />
)}
</ol>
</div> }
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>);.
{userList.map(v => (
<Card
key={v.user}
onClick={() =>
console.log("object")
}
>
<Card.Header
title={v.user}
thumb={require(`../img/${v[tableName].avatar}.png`)}
extra={<span>{v[tableName][extra]}</span>}
/>
<Card.Body>
{v[tableName].desc.split(/\n/g).map((v, k) => (
<li key={k}>{v}</li>
))}
</Card.Body>
</Card>
))}
Click events are not available on the .map() function in React
Card Component of React material UI does not support onClick props so you should not use it.
Quick Fix would be to enclose it inside a div and add onClick on the div as shown below:
<div onClick=> {() =>
console.log("object")
}>
<Card>
.....
</Card>
</div>
Or You can use some different library from :
https://material-ui.com/api/card-action-area/
I'm new in React and I trying to build a simple list of items clickable to get data and update the DOM, I have a list of links on render()
const listNews = this.state.news.map((item, i) =>
<ListGroupItem key={i} className="font-size-1 text-left">
<a href='#' onClick={() => this.getInfoNews(i)}>{item.title}</a>
</ListGroupItem>
);
the function "getInfoNews(i)" have this piece of code to display the data into DOM
getInfoNews(i){
var content = {
news : this.state.news[i]
}
console.log(content.news)
if(content.news === undefined){
return (
<Card>
<CardBody>
<CardTitle>Card Title</CardTitle>
<CardSubtitle>Card subtitle</CardSubtitle>
<CardText>Some quick example text to build on the card title and card's content.</CardText>
</CardBody>
</Card>
)
}else{
return (
<Card>
<CardBody>
<div className="container">
<img src={content.news.urlToImage} className="w-100" />
<CardTitle>
<div className="bottom-left font-size-2 bg-dark w-50 p-2 text-uppercase text-left">{content.news.title}</div>
</CardTitle>
</div>
<CardSubtitle className="text-right text-dark font-size-1 mr-4">
by {content.news.author ? content.news.author : "Anonymous"} , published at {content.news.publishedAt}
</CardSubtitle>
<CardText className="text-dark font-size-2 mt-4">
{content.news.description} read more
</CardText>
</CardBody>
</Card>
)
}
}
Work perfect on load first time, but dont work once clicked on every link, the data is loaded but the DOM dont update, some one can help me ? thanks!
React re-renders whenever there is an update to either the state or the props.
For example, you can load new data from API, and then do this.setState to update the component state. Then, react will re-render the component automatically.