ReactJS In a user profile display only fields that have a value - javascript

Objective
Displaying only the fields that are filled in.
Background
In my application people will first fill out the application which has fields like "early reg fee, early reg date, regular reg fee, regular reg date" and so after they fill out all the information and click "view profile" they will see all the fields whether it's filled out or not.
If the value of one of the fields is null or undefined then it would not show up in the profile.
I was trying to do this and I started of by creating a state in the constructor "this.state {value: ''}"
class CompetitionProfileView extends React.Component {
constructor(props) {
super(props);
this.state {value: ''}
this.getContactCard = this.getContactCard.bind(this);
}
getCompetitionValue(path) {
const value = _.get(this.props.competition, path);
return value ? value : '';
}
getCompetitionDateValue(path) {
const value = _.get(this.props.competition, path);
return value ? value.toDateString() : '';
}
getCompetitionTimeValue(path) {
const value = _.get(this.props.competition, path);
return value ? `${
value.getHours() - 12
}:${value.getMinutes()}` : '';
}
getContactCard(num) {
return
this.getCompetitionValue(`Information.contactFirstName${num}`) ?
<Card key={num} style={{backgroundColor: '#f9f9f9', width: '32%'}} zDepth={2}>
<CardTitle title={`${this.getCompetitionValue(`Information.contactFirstName${num}`)} ${this.getCompetitionValue(`Information.contactFirstName${num}`)}`} subtitle={`${this.getCompetitionValue('Information.contactPosition1')}`} />
<Divider/>
<CardText>
<p style={{display: 'flex', justifyContent: 'center'}}><Phone/>{`${this.getCompetitionValue(`Information.contactFirstName${num}`)}`}</p>
<p style={{display: 'flex', justifyContent: 'center'}}><Email/>{`${this.getCompetitionValue(`Information.contactFirstName${num}`)}`}</p>
</CardText>
</Card>
:
'';
}
render(actions) {
return (
<div>
<div className="profileheader" style={{display: 'flex', flexDirection: 'column'}}>
<Paper className='banner-image' style={{backgroundImage: `url(${this.getCompetitionValue('Resources.boardPicture.url')})`,backgroundSize: 'cover',width: '100%', height: '200px', backgroundPositionY: '20%'}} zDepth={3}>
{/* <br style={{lineHeight: '15'}}/> */}
</Paper>
<Paper className='text-main' style={{textAlign: 'center'}}>
<label>{this.getCompetitionValue('Information.name')}</label>
</Paper>
<Paper className='logo-image' style={{backgroundImage: `url(${this.getCompetitionValue('Resources.competitionLogo.url')})`, backgroundSize: 'cover', width: '100px', height: '100px', marginTop: '-110px', marginLeft: '3%', paddingbottom: '20px'}} zDepth={3}/>
</div>
<hr/>
<div style={{display: 'flex', justifyContent: 'space-between'}}>
<Card style={{backgroundColor: '#f9f9f9', width: '49%'}} zDepth={2}>
<RaisedButton style={{display: 'flex', justifyContent: 'center'}} primary={true} label="Application Packet" onClick={() => window.open(this.getCompetitionValue('Resources.applicationPacket.url'), '_blank')}/>
</Card>
<Card style={{backgroundColor: '#f9f9f9', width: '49%'}} zDepth={2}>
<RaisedButton style={{display: 'flex', justifyContent: 'center'}} primary={true} label="Audition Video Info" onClick={() => window.open(this.getCompetitionValue('Resources.auditionVideoInfo.url'), '_blank')}/>
</Card>
</div>
<br/>
<div className='mainbody' style={{display: 'flex', flexDirection: 'row', justifyContent: 'space-between'}}>
<br/>
<div className='rightbody' style={{display: 'flex', flexDirection: 'column', width: '60%', flexWrap: 'wrap'}}>
<Card style={{backgroundColor: '#F0EFEF'}} zDepth={2}>
<CardHeader title="About Us" />
<Divider/>
<CardText>{`${this.getCompetitionValue('Information.compBlurb')}`}</CardText>
</Card>
<br/>
<Card style={{backgroundColor: '#F0EFEF'}} zDepth={2}>
<CardHeader title="Application Information" />
<Divider/>
<CardText>
<p><b>Early Reg:</b>{` ${this.getCompetitionDateValue('Information.dueDateEarly')}`}</p>
<p><b>Early Reg Fee:</b>{` ${this.getCompetitionValue('Information.earlyDues')}`}</p>
<p><b>Regular Reg:</b>{` ${this.getCompetitionDateValue('Information.dueDateRegular')}`}</p>
<p><b>Regular Reg Fee:</b>{` ${this.getCompetitionValue('Information.regularDues')}`}</p>
<p><b>Late Reg:</b>{` ${this.getCompetitionDateValue('Information.dueDateLate')}`}</p>
<p><b>Late Reg Fee:</b>{` ${this.getCompetitionValue('Information.lateDues')}`}</p>
<p><b>Applications Due At:</b>{` ${this.getCompetitionTimeValue('Information.dueTime')}`}</p>
<p><b>Time Zone:</b>{` ${this.getCompetitionValue('Information.timeZone')}`}</p>
<p><b>Penalties:</b>{` ${this.getCompetitionValue('Information.extraFees')}`}</p>
<p><b>Hear Back Date:</b>{` ${this.getCompetitionDateValue('Information.hearbackDate')}`}</p>
<p><b>Payment Method:</b>{` ${this.getCompetitionValue('Information.paymentMethods')}`}</p>
<br/>
</CardText>
</Card>
</div>
</div>
<br/>
<div className="contactinfo" style={{display: 'flex', justifyContent: 'space-around'}}>
{[1,2,3].map((num) => this.getContactCard(num))}
</div>
<br/>
{this.props.competition.Board.length > 0 &&
<Card style={{backgroundColor: '#F0EFEF'}} zDepth={2}>
<Table >
<TableHeader adjustForCheckbox={false} displaySelectAll={false}>
<TableRow>
{ Object.keys(this.props.competition.Board[0]).map((key) => <TableHeaderColumn key={key}>{key}</TableHeaderColumn>) }
</TableRow>
</TableHeader>
<TableBody displayRowCheckbox={false}>
{this.props.competition.Board.map((row, i) => (
<TableRow key={i}>
{ Object.keys(row).map((column) => <TableRowColumn key={column}>{row[column].name ? row[column].name : row[column]}</TableRowColumn>) }
</TableRow>
))
}
</TableBody>
</Table>
</Card>
}
</div>
);
}
}
const mapStateToProps = (state) => {
return {
messages: state.messages
};
};
export default connect(mapStateToProps)(CompetitionProfileView);

You can use if statements in your render() function, like so:
render() {
if (this.props.thing1) {
return <h1>Thing 1</h1>
} else {
return (
<div className="warning">
<h2>Thing 2</h2>
</div>
)
}
}
You can even use functions in the render() function like this:
renderASmallPart() {
if (this.props.thing1) {
return <h1>Thing 1</h1>
} else {
return (
<div className="warning">
<h2>Thing 2</h2>
</div>
)
}
}
render() {
return (
<div>
<h1>My App</h1>
<h2>Here's a thing:</h2>
{this.renderASmallPart()}
</div>
)
}
You can use this to break up your large render() function into smaller functions that check what the value of their field is and only render something when the field has a non-empty value

You need to look into Conditional Rendering and only render that element when there is a value.
https://facebook.github.io/react/docs/conditional-rendering.html
https://atticuswhite.com/blog/render-if-conditionally-render-react-components/
http://devnacho.com/2016/02/15/different-ways-to-add-if-else-statements-in-JSX/
https://kylewbanks.com/blog/how-to-conditionally-render-a-component-in-react-native

Related

Trying to pass props to an axios call

I am building a forum and I want to make it where: Once you click on the title, it displays a page (which I already created) that displays the post title and then the post body. I used MUI to build the page as well. However, the Axios call fails when I call the backend and appending the "this.state.props." to the end.
My "All Questions" page code in which the user which select which post to open:
export default class DisplayPosts extends Component {
constructor(props) {
super(props);
this.state = {
posts: [],
selectedPostId: null,
};
}
componentDidMount (){
axios.get('http://localhost:6006/api/v1/posts')
.then(res=> {
const posts = [];
for (let key in res.data.data) {
posts.push({...res.data.data[key], id: key});
}
this.setState({
posts: posts,
})
console.log('Pulling From:: ', res.data.data)
})
.catch(err => console.log('err:: ', err)
)
}
onPostClickHandler = (_id) => {
this.setState({
selectedPostId: _id,
})
console.log(_id)
}
render() {
const posts = this.state.posts.map((post) => {
return <Posts
key ={post._id}
post={post}
postclicked = {this.onPostClickHandler.bind(
this,
post._id,
)} />;
})
return (
<Box component="main"
sx={{ flexGrow: 1, p: 3, marginLeft: "300px",marginTop:"-40px" }}>
<Toolbar />
<Stack spacing={2}>
<Typography>
<h1> All Questions </h1>
</Typography>
<Button
sx={{}}
variant = 'outlined'
size = 'medium'
color = 'secondary'>
Ask New Question
</Button>
<Divider />
<div>
{posts}
</div>
</Stack>
{this.state.selectedPostId && (
<ViewPosts _id={this.state.selectedPostId} />
)}
</Box>
)
}
}
My "View Posts" page, the page where the user will see the information of the post they just clicked
export default class ViewPosts extends Component {
constructor(props){
super(props);
this.state = {
post: null,
}
}
componentDidMount (){
axios.get(`http://localhost:6006/api/v1/posts/${this.props._id}`)
.then(res=> {
this.setState({
posts: {...res.data.data, _id: this.props._id}
})
console.log('Pulling From:: ', res.data.data)
})
.catch(err => console.log('err:: ', err)
)
}
render() {
return (
<div>
<><Box component="main"
sx={{ flexGrow: 1, p: 3, marginLeft: "300px", marginTop: "-40px" }}>
<Toolbar />
<Typography>
<h1>{this.state.post.title} </h1>
<p>Added: Today ..........Viewed: -- times </p>
</Typography>
<Divider />
<Stack direction='row' spacing={3}>
<Stack
direction="column"
spacing={2}>
<IconButton>
<KeyboardDoubleArrowUpIcon color='primary' />
</IconButton>
<IconButton sx={{ marginTop: '2px' }}>
<KeyboardDoubleArrowDownIcon color='primary' />
</IconButton>
</Stack>
<Typography>
<h6> </h6>
</Typography>
<Typography>
<p>
{this.state.post.body}
</p>
</Typography>
</Stack>
<Divider />
<TextField
sx={{ marginTop: "20px", marginLeft: "0px", width: '950px' }}
id="filled-multiline-static"
label="Enter Answer Here..."
multiline
rows={8}
variant="filled" />
<Button
sx={{ marginTop: "15px" }}
variant='contained'
size='large'
color='primary'
>
Post Your Answer
</Button>
</Box>
</>
</div>
)
}
}
From my understanding, componentDidMount is called after the component is mounted.
And by that, I mean the axios call will happen immediately, while the DOM content will take more time to load.
So, chances are, what happens is that you're not going to see anything, even if the axios call is finished and the state of the ViewPost is no longer null.
What you may wanna do now is to create a logic that prevents the DOM of the post from being displayed until the state is populated.
Like, for example...
render() {
return this.state.post && (
<div>
<><Box component="main"
sx={{ flexGrow: 1, p: 3, marginLeft: "300px", marginTop: "-40px" }}>
<Toolbar />
<Typography>
<h1>{this.state.post.title} </h1>
<p>Added: Today ..........Viewed: -- times </p>
</Typography>
<Divider />
<Stack direction='row' spacing={3}>
<Stack
direction="column"
spacing={2}>
<IconButton>
<KeyboardDoubleArrowUpIcon color='primary' />
</IconButton>
<IconButton sx={{ marginTop: '2px' }}>
<KeyboardDoubleArrowDownIcon color='primary' />
</IconButton>
</Stack>
<Typography>
<h6> </h6>
</Typography>
<Typography>
<p>
{this.state.post.body}
</p>
</Typography>
</Stack>
<Divider />
<TextField
sx={{ marginTop: "20px", marginLeft: "0px", width: '950px' }}
id="filled-multiline-static"
label="Enter Answer Here..."
multiline
rows={8}
variant="filled" />
<Button
sx={{ marginTop: "15px" }}
variant='contained'
size='large'
color='primary'
>
Post Your Answer
</Button>
</Box>
</>
</div>
)
}
}

how to show search results inside existed list

I have a tracklist that I fetched from API
I implement search bar that is responsible for looking for according track
But the problem is that when I input anything in search bar, another tracklist appears and it's filtered same way I wanted to filter already existed tracklist with the name of track and artist.
I thougt I need to use map in map method.
const TrackList = ({trackList}) => {
const [searchTerm, setSerchTerm] = useState('')
const [searchRes, setSearchRes] = useState([])
const settingSearch = (e) => {
e.preventDefault()
setSerchTerm(e.target.value)
}
useEffect(() => {
const res = trackList.map(i=> {
return i.name
})
const filteredRes = res.filter(item => item.toLowerCase().includes(searchTerm))
setSearchRes(filteredRes)
}, [searchTerm])
return (
<div>
<Container>
<h1 className='mb-5 mt-5'>Top TrackList</h1>
<FormControl type="text"
placeholder="Search"
className="mr-sm-2"
value={searchTerm}
onChange={settingSearch}
/>
<ul>
{ searchRes.map(i => (
<li>{i}</li>
)) }
</ul>
{trackList.map(item => {
return (
<Row className='mt-1' style={{padding: '5px', border: '1px solid #000', display: 'flex', justifyContent: 'flex-start', alignItems: 'center'}}>
<Col lg={1} md={1} sm={1}>
<a href={item.artist.url}><img src={item.image[1]['#text']} /></a>
</Col>
<Col lg={11} md={11} sm={11}>
<Link to={`/artist/${item.artist.name}`}><h6>{item.artist.name}</h6></Link>
<p>"{item.name}"</p>
</Col>
</Row>
)
})}
</Container>
</div>
)
}
This is because you map over both trackList and searchRes.
You may want to set searchRes equal to the array in trackList when the page renders like this
const [searchRes, setSearchRes] = useState(trackList)
And then use it in your map like this
{searchRes.map(item => {
return (<Row className='mt-1' style={{padding: '5px', border: '1px solid #000', display: 'flex', justifyContent: 'flex-start', alignItems: 'center'}}>
...
And remove this
<ul>
{ searchRes.map(i => (
<li>{i}</li>
)) }
</ul>
Let me know if you need any more explanation.
Try this, you dont need useeffect just set filtered array onchange input and add next time pls all files what you use like Container, FormControl i dont know exactly what each of it is and it hard to check is it correct
const TrackList = ({trackList}) => {
const [searchRes, setSearchRes] = useState(trackList);
const settingSearch = (e) => {
e.preventDefault();
const filteredRes = trackList.filter(item => item.name.toLowerCase().includes(e.target.value));
setSearchRes(filteredRes)
};
return (
<div>
<Container>
<h1 className='mb-5 mt-5'>Top TrackList</h1>
<FormControl type="text"
placeholder="Search"
className="mr-sm-2"
onChange={settingSearch}
/>
{searchRes.map(item => (
<Row className='mt-1' style={{padding: '5px', border: '1px solid #000', display: 'flex', justifyContent: 'flex-start', alignItems: 'center'}}>
<Col lg={1} md={1} sm={1}>
<a href={item.artist.url}><img src={item?.image?.[1]['#text']} /></a>
</Col>
<Col lg={11} md={11} sm={11}>
<Link to={`/artist/${item.artist.name}`}><h6>{item.artist.name}</h6></Link>
<p>{item.name}</p>
</Col>
</Row>
))}
</Container>
</div>
)
};

Add to cart function using react

I am building a search filter of sorts using Elasticsearch and ReactiveSearch for UI.
Each result has an ID, title, etc that I can call to display it when a user searches. I need a button for each results, and when the user clicks it, it should 'add to cart' or something like that. I just need the clicked results to be shown in the next page.
The code below is my search field, and the results that are shown. I have an onclick function, but I don't know how to proceed after that. I think I need to use redux.
class Results extends Component {
render() {
return (
<ReactiveBase
app="datataglist"
credentials="mRgWyoKGQ:f47be2a6-65d0-43b6-8aba-95dbd49eb882"
url="https://scalr.api.appbase.io"
>
<DataSearch
componentId="search"
dataField={[
"maker_tag_name",
"maker_tag_name.autosuggest",
"maker_tag_name.keyword"
]}
fieldWeights={[6, 2, 6]}
fuzziness={1}
highlightField={["maker_tag_name"]}
placeholder="Search Tag Name"
style={{
marginBottom: 20
}}
title="Maker Tag Name"
/>
<SelectedFilters />
<ReactiveList
componentId="results"
dataField="_score"
pagination={true}
react={{
and: ["system", "grouping", "unit", "search"]
}}
size={10}
noResults="No results were found..."
renderItem={renderItem}
/>
</ReactiveBase>
);
}
}
function getNestedValue(obj, path) {
const keys = path.split(".");
const currentObject = obj;
const nestedValue = keys.reduce((value, key) => {
if (value) {
return value[key];
}
return "";
}, currentObject);
if (typeof nestedValue === "object") {
return JSON.stringify(nestedValue);
}
return nestedValue;
}
function renderItem(res, triggerClickAnalytics) {
let { url, unit, title, system, score, proposed, id } = {
title: "maker_tag_name",
proposed: "proposed_standard_format",
unit: "units",
system: "system",
score: "_score",
url: "",
id: "_id"
};
title = getNestedValue(res, title);
system = getNestedValue(res, system);
url = getNestedValue(res, url);
unit = getNestedValue(res, unit);
score = getNestedValue(res, score);
proposed = getNestedValue(res, proposed);
id = getNestedValue(res, id);
return (
<Row
onClick={triggerClickAnalytics}
type="flex"
gutter={16}
key={res._id}
style={{ margin: "20px auto", borderBottom: "1px solid #ededed" }}
>
<Col style={{ width: "360px" }}>
<h3
style={{ fontWeight: "600" }}
dangerouslySetInnerHTML={{
__html: title || "Choose a valid Title Field"
}}
/>
</Col>
<div style={{ padding: "20px" }} />
<Col>
<p
style={{ fontSize: "1em", width: "300px" }}
dangerouslySetInnerHTML={{
__html: system || "Choose a valid Description Field"
}}
/>
</Col>
<div style={{ padding: "10px" }} />
<Col>
<p
style={{ fontSize: "1em" }}
dangerouslySetInnerHTML={{
__html: unit || "-"
}}
/>
</Col>
<div style={{ padding: "10px" }} />
<Col style={{ minWidth: "120px" }}>
<p
style={{ fontSize: "1em", width: "300px"}}
dangerouslySetInnerHTML={{
__html: proposed || "Choose a valid Description Field"
}}
/>
</Col>
<div style={{ padding: "10px" }} />
<Col>
<p
style={{ fontSize: "1em"}}
dangerouslySetInnerHTML={{
__html: Math.round(score) || "Choose a valid Description Field"
}}
/>
</Col>
<Col>
<Button
shape="circle"
icon={<CheckOutlined />}
style={{ marginRight: "5px" }}
onClick={()=>{this.handleClick(id)}}
/>
</Col>
<Col style={{ minWidth: "120px" }}>
<p
style={{ fontSize: "1em", width: "300px"}}
dangerouslySetInnerHTML={{
__html: id || "Choose a valid Description Field"
}}
/>
</Col>
</Row>
);
}
export default Results;
Any help would be appreciated.

Show length of an array based on what is left in array after its sliced

I have a react component that upon clicking showMore. it will load more comments. The issue im facing is that
View {showMore} More Comments
is not showing the items that are left in the array. Currently there are 7 comments in an array, and if you click show more, it will initially read show 3 more, but when i click again it says show 6 more. when it should be a lesser number than 6. It should be like show 2 more, etc. I'm quite confused on how to go about writing this logic.
What am i doing wrong
CommentList.tsx
import React, { Fragment, useState } from "react";
import Grid from "#material-ui/core/Grid";
import List from "#material-ui/core/List";
import Typography from "#material-ui/core/Typography";
import CommentItem from "./../commentItem/CommentItem";
import moment from "moment";
import OurLink from "../../../common/OurLink";
import OurSecondaryButton from "../../../common/OurSecondaryButton";
import OurModal from "../../../common/OurModal";
.....
function CommentList(props: any) {
const [showMore, setShowMore] = useState<Number>(3);
const [openModal, setOpenModal] = useState(false);
const [showLessFlag, setShowLessFlag] = useState<Boolean>(false);
const the_comments = props.comments.length;
const inc = showMore as any;
const showComments = (e) => {
e.preventDefault();
if (inc + 3 <= the_comments) {
setShowMore(inc + 3);
} else {
setShowMore(the_comments);
}
// setShowLessFlag(true);
};
........
const showMoreComments = () => {
return props.comments
.slice(0, showMore)
.sort((a, b) => a.id - b.id)
.map((comment, i) => (
<div key={i}>
<List style={{ paddingBottom: "20px" }}>
<img alt="gravatar" style={{ margin: "-10px 15px" }} src={comment.author.gravatar} width="30" height="30" />
<Typography style={{ display: "inline-block", fontWeight: 700, padding: "5px 0px" }} variant="h6" align="left">
{Object.entries(props.currentUser).length === 0 ? (
<Fragment>
<span style={{ cursor: "pointer", fontSize: "12px", fontWeight: isBold(comment) }} onClick={handleClickOpen}>
{comment.author.username}
</span>
{comment.userId === props.userId && <span style={{ fontSize: "12px" }}> (OP)</span>}
{openModal ? <OurModal open={openModal} handleClose={handleCloseModal} /> : null}
</Fragment>
) : (
<Fragment>
<OurLink
style={{ fontSize: "12px", fontWeight: isBold(comment) }}
to={{
pathname: `/profile/${comment.author.username}`,
}}
title={comment.author.username}
/>
{comment.userId === props.userId && <span style={{ fontSize: "12px" }}> (OP)</span>}
</Fragment>
)}
</Typography>
<div style={ourStyle}>
<CommentItem comment={comment} user={props.user} postId={props.postId} {...props} />
<Typography style={{ fontSize: "12px" }} variant="body1" align="left">
{moment(comment.createdAt).calendar()}
</Typography>
</div>
</List>
</div>
));
};
console.log(props.comments.slice(0, showMore).length);
return (
<Grid>
<Fragment>
<div style={{ margin: "30px 0px" }}>
<OurSecondaryButton onClick={(e) => showComments(e)} component="span" color="secondary">
View {showMore} More Comments
</OurSecondaryButton>
</div>
</Fragment>
{showLessFlag === true ? (
// will show most recent comments below
showMoreComments()
) : (
<Fragment>
{/* filter based on first comment */}
{props.comments
.filter((item, i) => item)
.sort((a, b) => b.id - a.id)
.slice(0, showMore)
.map((comment, i) => (
<div key={i}>
<List style={{ paddingBottom: "20px" }}>
<img alt="gravatar" style={{ margin: "-10px 15px" }} src={comment.author.gravatar} width="30" height="30" />
<Typography style={{ display: "inline-block", fontWeight: 700, padding: "5px 0px" }} variant="h6" align="left">
{Object.entries(props.currentUser).length === 0 ? (
<Fragment>
<span style={{ fontSize: "12px", cursor: "pointer", fontWeight: isBold(comment) }} onClick={handleClickOpen}>
{comment.author.username}
{comment.userId === props.userId && <span style={{ fontSize: "12px" }}> (OP)</span>}
</span>
{openModal ? <OurModal open={openModal} handleClose={handleCloseModal} /> : null}
</Fragment>
) : (
<Fragment>
<OurLink
style={{ fontSize: "12px", fontWeight: isBold(comment) }}
to={{
pathname: `/profile/${comment.author.username}`,
}}
title={comment.author.username}
/>
{comment.userId === props.userId && <span style={{ fontSize: "12px" }}> (OP)</span>}
</Fragment>
)}
</Typography>
<div style={ourStyle}>
<CommentItem comment={comment} user={props.user} postId={props.postId} {...props} />
<Typography style={{ fontSize: "12px" }} variant="body1" align="left">
{moment(comment.createdAt).calendar()}
</Typography>
</div>
</List>
</div>
))}
</Fragment>
)}
</Grid>
);
}
// prevents un-necesary re renders
export default React.memo(CommentList);
You want to show 3 more comments each time, or 1-2 items if there are less than 3 items left. So "View 3 More comments" if there are more than 3 left, or "View 1/2 More Comments" if there are only 1 or 2 left.
Or in other words cap the number of new comments shown at 3:
the minimum value of either 3 or (total number of comments - current shown comments = number of comments left).
View {Math.min(3, the_comments - inc)} More Comments

Reactjs Common state

I'm new to reactjs and I want an e-trade project. The problem is pressing the first "+" button, but increasing the number in the box next to it. How can I do this?
When I press the first, the other shouldn't increase because I will add more vegetable.
How can I solve this? Thank you so much
class App extends Component {
constructor(props) {
super(props);
this.state = {
counter: 0
};
}
increment() {
this.setState(prevState => ({
counter: this.state.counter + 1
}));
}
decrement() {
if (this.state.counter === 0) {
this.setState({
counter: 0
});
} else {
this.setState(prevState => ({
counter: this.state.counter - 1
}));
}
}
render() {
return (
<div class="container">
<div class="row" style={{ marginTop: '50px' }}>
<Card className="col-md-3" style={{ }}>
<Card.Img variant="top" src="https://res.cloudinary.com/sivadass/image/upload/v1493620046/dummy-products/broccoli.jpg" style={{ width: '200px',marginLeft:'30px' }}/>
<Card.Body>
<Card.Title style={{ textAlign: 'center' }}> Brocoli - 1
</Card.Title>
<Card.Text style={{ textAlign: 'center' }}>
1 $
</Card.Text>
<div style={{ textAlign: 'center' }}>
<Button onClick={this.decrement.bind(this)}>-</Button>
<h2 value="1">{this.state.counter}</h2>
<Button onClick={this.increment.bind(this)}>+</Button>
</div>
</Card.Body>
</Card>
<Card className="col-md-3" style={{ marginLeft:'10px' }}>
<Card.Img variant="top" src="https://res.cloudinary.com/sivadass/image/upload/v1493620046/dummy-products/broccoli.jpg" style={{ width: '200px',marginLeft:'30px' }}/>
<Card.Body>
<Card.Title style={{ textAlign: 'center' }}> Brocoli - 1
</Card.Title>
<Card.Text style={{ textAlign: 'center' }}>
1 $
</Card.Text>
<div style={{ textAlign: 'center' }}>
<Button onClick={this.decrement.bind(this)}>-</Button>
<h2 value="1">{this.state.counter}</h2>
<Button onClick={this.increment.bind(this)}>+</Button>
</div>
</Card.Body>
</Card>
</div>
</div>
)
}
}
export default App;
Can you please check whether you are looking for the following type of solution or not? Please ignore the styling I have just implemented the functionality.
constructor(props) {
super(props);
this.state = {
items:[{
name:"Veg 1",
counter:0
},{
name:"Veg 2",
counter:0
}]
};
}
increment = (index) =>{
let localItems = [...this.state.items];
localItems[index].counter += 1;
this.setState({
...this.state,
items:[...localItems]
})
}
decrement = (index) =>{
let localItems = [...this.state.items];
localItems[index].counter -= 1;
this.setState({
...this.state,
items:[...localItems]
})
}
render() {
const { classes } = this.props;
return (
this.state.items.length ? this.state.items.map((item,index)=>{
return (
<div>
<button onClick={()=>this.increment(index)}>+</button>
<span>{item.name} - {item.counter}</span>
<button onClick={()=>item.counter && this.decrement(index)}>-</button>
</div>
)
}):null
)
}

Categories