This is the code for a navigation in tab header using an arrow icon, but its moving to only 2 items and not going to the third item.
This is the onclick function and useState
const [ActiveTab, setActiveTab] = useState("HAIRCUT");
var index = 0;
const HandleTab = (data) => {
index = (index + 1) % data.length;
console.log(data[index].category_name);
setActiveTab(data[index].category_name);
};
This is the icon and TabConatiner section
<MainRow>
{isServicesLoading || services.length === 0 || services.data === undefined ? (
<></>
) : (
<>
{" "}
{services.length !== 0 &&
services.data?.map((category) => (
<>
<div className="lefticon" style={{}}>
<img src={Left} />
</div>
<div onClick={() => HandleTab(services.data)} className="righticon">
<img src={Right} />
</div>
</>
))}
<TabContainer
defaultActiveKey="HAIRCUT"
activeKey={ActiveTab}
onSelect={() => HandleTab(services.data)}
id="services-tabs"
>
{services.length !== 0 &&
services.data?.map((category, idx) => (
<TabItem
eventKey={category.category_name}
title={
<>
<TitleContainer>
<TitleImg src={service[idx].img} />
{/* <TitleImg src={category.icon} /> */}
<H3>{category.category_name}</H3>
</TitleContainer>
<TitleContainer>
{/* <TitleImg src={service[idx].img} /> */}
</TitleContainer>
</>
}
>
<div>
<Col md={12} sm={12}>
<Link to={`/service/${category.slug}`}>
{" "}
<TabContent
className="tabservices"
service={category.service}
/>
</Link>
</Col>
</div>
</TabItem>
))}
</TabContainer>
</>
)}
</MainRow>
Ant modal appears few times, but if i remove map function inside the modal then its works fine its appears only one time.
what may be the issue? please help me to solve this issue (Modal triggering/appears few times)
Ant modal code follows:
const [visibleVoucherModal, setVisibleVoucherModal] = useState(false);
const showVoucherModal = () => {
setVisibleVoucherModal(true)
}
const closeVoucherModal = () => {
setVisibleVoucherModal(false);
};
const SavedVoucherModal = () => {
return (
<Modal title="Select Any Voucher" visible={visibleVoucherModal} onCancel={closeVoucherModal} footer={null} >
{savedVoucherList.length >= 1 ?
<div>
<Checkbox.Group className="w-100" onChange={checkBox} value={voucherVal} >
{savedVoucherList.map((item, i) => {
return (
<div key={i}>
<Checkbox value={item.Id} className="w-100" onChange={e => { handleCheckBox(e, i) }}
checked={checked[i] || true} disabled={!checked[i] && disabled} >
<div className="pl-4">
<List.Item style={{ marginTop: "-35px" }} className="py-0">
<List.Item.Meta
// avatar={<img src={item.Photo == '' ? 'd' : item.Photo} alt="" width="72" />}
title={<span style={{ fontSize: "12px" }}>{item.Name}</span>}
description={<div className="small">Expiry Date: <br />{item.ExpireDate.slice(0, 10)}</div>}
/>
<div className="pt-1">
<p className="font-weight-bold text-primary mb-0">{numberFormat(item.Price)}</p>
<small><del>{numberFormat(item.OldPrice)}</del></small>
</div>
</List.Item>
</div>
</Checkbox>
<Divider className="m-0 p-0 mb-4" />
</div>)
})}
</Checkbox.Group>
</div> :
<p className="mb-4">Oops, there is no voucher applicable to this order</p>
}
<Button type="primary" className="font-weight-bold btn-round" onClick={closeVoucherModal}>Close</Button>
</Modal>
)
}
Button:
<Button type="default" className="text-primary border-right-0 border-left-0" size="large" onClick={showVoucherModal}>Redeem Voucher</Button>
I have a list of icons.
When I click on one of them, I want it to :
1/ change state to day or night
2/ change icon image to a sun or a moon.
But with the code that I've written, if I click on one, it changes all the icons image and only one state is linked to all.
How can I do so each icons has it own state and when i click on icon, only the icon clicked changes to a moon or a sun and not all of them ?
My code :
export default function MyModal() {
const [isVisible, setVisible] = useState(false);
const [isDay, setDay] = useState(true);
const { RangePicker } = DatePicker;
const { Option } = Select;
const style = {
verticalAlign: "middle",
marginRight: 10,
};
function handleDayNight() {
isDay === true ? setDay(false) : setDay(true);
console.log("isDay =", isDay);
}
function handleSelectChange(value) {
console.log(`selected ${value}`);
}
function handleCheckChange(checkedValues) {
console.log("checked =", checkedValues);
}
return (
<div>
<Button type="primary" onClick={() => setVisible(true)}>
Add an exception
</Button>
<Modal
title="Add an exception"
style={{ top: 20 }}
visible={isVisible}
onOk={() => setVisible(false)}
onCancel={() => setVisible(false)}
>
<p>Exceptions name</p>
<Input placeholder="80% Wednesday" />
<p style={{ marginTop: 10 }}>Select date</p>
<RangePicker onChange={([date]) => console.log(date)} />
<p style={{ marginTop: 10 }}>Frequency</p>
<Select
defaultValue="Weekly"
style={{ width: 120 }}
onChange={handleSelectChange}
>
<Option value="daily">Daily</Option>
<Option value="weekly">Weekly</Option>
<Option value="monthly">Monthly</Option>
</Select>
<Divider />
<Checkbox.Group style={{ width: "100%" }} onChange={handleCheckChange}>
<div>
{isDay === true ? (
<Sun style={style} onClick={handleDayNight} />
) : (
<Moon style={style} onClick={handleDayNight} />
)}
<Checkbox value="Monday">Monday</Checkbox>
<br></br>
{isDay === true ? (
<Sun style={style} onClick={handleDayNight} />
) : (
<Moon style={style} onClick={handleDayNight} />
)}
<Checkbox value="Tuesday">Tuesday</Checkbox>
<br></br>
{isDay === true ? (
<Sun style={style} onClick={handleDayNight} />
) : (
<Moon style={style} onClick={handleDayNight} />
)}
<Checkbox value="Wednesday">Wednesday</Checkbox>
<br></br>
{isDay === true ? (
<Sun style={style} onClick={handleDayNight} />
) : (
<Moon style={style} onClick={handleDayNight} />
)}
<Checkbox value="Thursday">Thursday</Checkbox>
<br></br>
<Checkbox value="Friday">Friday</Checkbox>
<br></br>
<Checkbox value="Saturday">Saturday</Checkbox>
<br></br>
<Checkbox value="Sunday">Dimanche</Checkbox>
</div>
</Checkbox.Group>
</Modal>
</div>
);
}
Use a separate component for each of those, so you can have individual separate states inside those components. Eg, replace all of
{isDay === true ? (
<Sun style={style} onClick={handleDayNight} />
) : (
<Moon style={style} onClick={handleDayNight} />
)}
with
<SunMoon style={style} />
const SunMoon = ({ style }) => {
const [isDay, setDay] = useState(true);
const handleDayNight = () => setDay(!isDay);
return isDay
? <Sun style={style} onClick={handleDayNight} />
: <Moon style={style} onClick={handleDayNight} />;
};
I currently have a search input which filters through the github repos. I then have a drop down select which allows a user to filter based on the language of the code used. I instead would like to use buttons rather than a drop down. Is there a way to filter the results on click as opposed to onChange like I am doing with the drop down. My code is as follows:
const Profile = () => {
const [formData, setFormData] = useState(INITIAL_STATE)
const [updated, setUpdated] = useState(false)
const [created, setCreated] = useState(false)
const { data } = useContext(GithubContext)
const handleUpdated = () => {
setUpdated(!updated)
data &&
data.sort((a, b) => {
if (updated) return a.updated_at > b.updated_at ? -1 : 1
return a.updated_at > b.updated_at ? 1 : -1
})
}
const handleCreated = () => {
setCreated(!created)
data &&
data.sort((a, b) => {
if (created) return a.created_at > b.created_at ? -1 : 1
return a.created_at > b.created_at ? 1 : -1
})
}
const handleInputChange = field => e => {
setFormData({ ...formData, [field]: e.target.value })
}
const classes = useStyles()
return (
<>
<div style={{ marginTop: 85, marginBottom: 85 }}>
<Container className={classes.dashboardContainer}>
<Card className={classes.card} style={{ width: '100%' }}>
<CardContent className={classes.content}>
<div className={classes.form}>
<Grid
container
spacing={2}
alignItems='center'
justify='space-between'
>
<Grid item sm={4} xs={12} className={classes.grid}>
<SelectStatus
language={formData.language}
handleInputChange={handleInputChange}
/>
</Grid>
<Grid item sm={4} xs={12} className={classes.grid}>
<TextField
className={classes.jobField}
margin='normal'
fullWidth
id='search'
name='search'
label='Search by Title'
placeholder='Search by Title'
onChange={handleInputChange('search')}
value={formData.search}
/>
</Grid>
<Grid item sm={2} xs={12} className={classes.grid}>
<Button
variant='contained'
color='primary'
onClick={handleUpdated}
>
Updated {updated ? '(oldest)' : '(newest)'}
</Button>
</Grid>
<Grid item sm={2} xs={12} className={classes.grid}>
<Button
fullWidth
variant='contained'
color='primary'
onClick={handleCreated}
>
Created {created ? '(oldest)' : '(newest)'}
</Button>
</Grid>
</Grid>
</div>
</CardContent>
</Card>
<div
style={{
textAlign: 'center'
}}
>
<Button variant='outlined' color='primary'>
JavaScript
</Button>
<Button variant='outlined' color='primary'>
Primary
</Button>
<Button variant='outlined' color='primary'>
Secondary
</Button>
<Button variant='outlined' color='primary'>
Disabled
</Button>
<Button variant='outlined' color='primary'>
Link
</Button>
<br />
</div>
</Container>
{!data ? (
<h1 className={classes.loading}>Initializing Repos...</h1>
) : (
<Container style={{ padding: 10 }}>
{!data ? (
<div style={{ placeItems: 'center' }}>Loading...</div>
) : (
<Grid container alignItems='center' spacing={4}>
{data &&
data
.filter(data => {
if (formData.language === 'All') return true
return data.language === formData.language
})
.filter(data => {
if (formData.search === '') return true
return (data.name + data.language)
.toLowerCase()
.includes(formData.search.toLowerCase())
})
.map(user => <RepoCard key={user.id} user={user} />)}
</Grid>
)}
</Container>
)}
</div>
</>
)
}
export default Profile
and here is the drop down component:
import React, { useRef } from "react"
// MUI stuff
import Select from "#material-ui/core/Select"
import InputLabel from "#material-ui/core/InputLabel"
import MenuItem from "#material-ui/core/MenuItem"
import FormControl from "#material-ui/core/FormControl"
const SelectStatus = ({ language, handleInputChange }) => {
const inputLabel = useRef(null)
return (
<FormControl style={{ width: "100%" }}>
<InputLabel ref={inputLabel} id='demo-simple-select-outlined-label'>
Status
</InputLabel>
<Select
labelId='demo-simple-select-outlined-label'
id='demo-simple-select-outlined'
value={language}
onChange={handleInputChange("language")}
fullWidth
>
<MenuItem value='All'>All</MenuItem>
<MenuItem value='HTML'>HTML</MenuItem>
<MenuItem value='JavaScript'>JavaScript</MenuItem>
<MenuItem value='Ruby'>Ruby</MenuItem>
</Select>
</FormControl>
)
}
export default SelectStatus
Any suggestions would be extremely helpful!!!
You can just use buttons and add them a value so it will work exactly the same as the dropdown
const handleInputChange = field => e => {
setFormData({ ...formData, [field]: e.target.value })
}
<Button onClick={handleInputChange('language')} value="All">All</Button>
<Button onClick={handleInputChange('language')} value="HTML")>HTML</Button>
<Button onClick={handleInputChange('Javascript')} value="Javascript">JavaScript</Button>
<Button onClick={handleInputChange('Ruby')} value="Ruby">Ruby</Button>
I have a component that scrolls to view after a comment has been submitted.
const commentSubmit = (e: any, id: number) => {
......
divRef.current.scrollIntoView({ behavior: "smooth" });
};
It scrolls to the comment where <div ref={divRef]></div> is placed which is at the bottom of the most recent comment. The probelem is that if i make a new post, it will scroll into the wrong div element. So its just scrolling at the very bottom. How can i tell ref to scroll to the a specific element based on what post the comment belongs to ?
{post.Comments.length > 0 ? (
<Fragment>
<Typography style={{ padding: "10px 0px", margin: "20px 0px" }}>Commments</Typography>
// ignore this divRef in comment list its not being called in the comment list.
<CommentList ref={divRef} user={currentUser} deleteComment={props.deleteComment} userId={post.userId} postId={post.id} comments={post.Comments} {...props} />
{/* if show more hide show more button and show show less comments button */}
</Fragment>
) : (
<Grid item={true} sm={12} lg={12} style={{ padding: "30px 0px" }}>
<Typography>No Commments Yet</Typography>
</Grid>
)}
<div ref={divRef}></div>
postItemContainer.tsx
import React, { Fragment, useState, useCallback, useRef } from "react";
import Avatar from "#material-ui/core/Avatar";
import Button from "#material-ui/core/Button";
import Grid from "#material-ui/core/Grid";
import Paper from "#material-ui/core/Paper";
import Typography from "#material-ui/core/Typography";
import DeleteOutlineOutlinedIcon from "#material-ui/icons/DeleteOutlineOutlined";
import FavoriteIcon from "#material-ui/icons/Favorite";
import FavoriteBorderIcon from "#material-ui/icons/FavoriteBorder";
import moment from "moment";
import { toast, ToastContainer } from "react-toastify";
import OurLink from "../../../common/OurLink";
import CommentForm from "../comment/CommentForm";
import CommentList from "../commentList/CommentList";
import OurModal from "../../../common/OurModal";
import "react-toastify/dist/ReactToastify.css";
function PostItemContainer(props: any) {
const [openModal, setOpenModal] = useState(false);
const [openForm, setOpenForm] = useState(false);
const [comment_body, setCommentBody] = useState("");
const [gifUrl, setGifUrl] = useState("");
const divRef = React.useRef<any>();
const writeComment = () => {
// this is the same as this.setState({ openForm: !this.state.open })
setOpenForm(!openForm);
};
const commentChange = (comment) => {
setGifUrl("");
setCommentBody(comment);
};
const selectGif = (e) => {
setGifUrl(e.images.downsized_large.url);
setCommentBody("");
// you wont be able to add text comment with a gif, it will look weird :(
};
const handleClickOpen = () => {
setOpenModal(true);
};
const handleCloseModal = () => {
setOpenModal(false);
};
const commentSubmit = (e: any, id: number) => {
e.preventDefault();
const formData = {
comment_body,
id,
gifUrl,
};
props.postComment(formData);
setCommentBody("");
setOpenForm(false);
console.log(divRef);
divRef.current.scrollIntoView({ behavior: "smooth" });
};
const { post, currentUser, getNotifications } = props;
console.log(divRef);
return (
<Fragment>
{getNotifications && <ToastContainer autoClose={1000} position={toast.POSITION.BOTTOM_RIGHT} />}
<Grid item={true} sm={12} md={12} style={{ margin: "20px 0px" }}>
<Paper style={{ padding: "20px" }}>
<Typography variant="h5" align="left">
<OurLink
style={{ fontSize: "16px" }}
to={{
pathname: `/post/${post.id}`,
state: { post },
}}
title={post.title}
/>
</Typography>
<Grid item={true} sm={12} md={12} style={{ padding: "30px 0px" }}>
<Typography align="left">{post.postContent.slice(0, 50)}</Typography>
</Grid>
<Avatar
style={{
display: "inline-block",
margin: "-10px -20px",
padding: "0px 30px 0px 20px",
}}
sizes="small"
src={post.author.gravatar}
/>
<Typography display="inline" variant="subtitle1" align="left">
<OurLink
to={{
pathname: `/profile/${post.author.username}`,
state: { post },
}}
title={post.author.username}
/>
</Typography>
<Typography align="right">Likes: {post.likeCounts}</Typography>
<Grid container={true} spacing={1} style={{ padding: "20px 0px" }}>
<Grid item={true} sm={10} lg={10} md={10} style={{ padding: "0px 0px" }}>
<Typography align="left">
{currentUser && currentUser.user && post.userId === currentUser.user.id ? (
<span style={{ cursor: "pointer" }} onClick={() => props.deletePost(post.id, post.userId)}>
<DeleteOutlineOutlinedIcon style={{ margin: "-5px 0px" }} color="primary" /> <span>Delete</span>
</span>
) : null}
</Typography>
</Grid>
<Grid item={true} sm={2} lg={2} style={{ padding: "0px 15px" }}>
<Typography align="right">
{Object.entries(currentUser).length === 0 ? (
<Fragment>
<span onClick={handleClickOpen}>
<FavoriteBorderIcon style={{ color: "red", cursor: "pointer" }} />
</span>
{openModal ? <OurModal open={openModal} handleClose={handleCloseModal} /> : null}
</Fragment>
) : (
<Fragment>
{post.likedByMe === true ? (
<span style={{ cursor: "pointer" }} onClick={() => props.dislikePost(post.id)}>
<FavoriteIcon style={{ color: "red" }} />
</span>
) : (
<span onClick={() => props.likePost(post.id)}>
<FavoriteBorderIcon style={{ color: "red", cursor: "pointer" }} />
</span>
)}
</Fragment>
)}
</Typography>
</Grid>
</Grid>
<Typography variant="h6" align="left">
{moment(post.createdAt).calendar()}
</Typography>
<Grid item={true} sm={12} lg={12} style={{ paddingTop: "40px" }}>
{Object.entries(currentUser).length === 0 ? (
<Fragment>
<Button onClick={handleClickOpen} variant="outlined" component="span" color="primary">
{"Write A Comment"}
</Button>
{openModal ? <OurModal open={openModal} handleClose={handleCloseModal} /> : null}
</Fragment>
) : (
<Fragment>
<Button onClick={writeComment} variant="outlined" component="span" color="primary">
{openForm ? "Close" : "Write A Comment"}
</Button>
</Fragment>
)}
{openForm ? (
<CommentForm
commentChange={(e: any) => commentChange(e.target.value)}
comment_body={comment_body}
onSubmit={(e) => commentSubmit(e, post.id)}
gifUrl={selectGif}
isGif={gifUrl}
/>
) : null}
{post.Comments.length > 0 ? (
<Fragment>
<Typography style={{ padding: "10px 0px", margin: "20px 0px" }}>Commments</Typography>
<CommentList ref={divRef} user={currentUser} deleteComment={props.deleteComment} userId={post.userId} postId={post.id} comments={post.Comments} {...props} />
{/* if show more hide show more button and show show less comments button */}
</Fragment>
) : (
<Grid item={true} sm={12} lg={12} style={{ padding: "30px 0px" }}>
<Typography>No Commments Yet</Typography>
</Grid>
)}
<div ref={divRef}></div>
</Grid>
</Paper>
</Grid>
</Fragment>
);
}
export default PostItemContainer
CommentList.tsx(just in case) how the comments are bing mapped and iterated
import React, { Fragment, useState } from "react";
import Grid from "#material-ui/core/Grid";
import OurSecondaryButton from "../../../common/OurSecondaryButton";
import CommentListContainer from "../commentListContainer/commentListContainer";
function CommentList(props: any) {
const [showMore, setShowMore] = useState<Number>(2);
const [openModal, setOpenModal] = useState(false);
const [showLessFlag, setShowLessFlag] = useState<Boolean>(false);
const the_comments = props.comments.length;
const inc = showMore as any;
const min = Math.min(2, the_comments - inc);
const showComments = (e) => {
e.preventDefault();
if (inc + 2 && inc <= the_comments) {
setShowMore(inc + 2);
setShowLessFlag(true);
} else {
setShowMore(the_comments);
}
};
const handleClickOpen = () => {
setOpenModal(true);
};
const handleCloseModal = () => {
setOpenModal(false);
};
const showLessComments = (e) => {
e.preventDefault();
setShowMore(2);
setShowLessFlag(false);
};
const isBold = (comment) => {
return comment.userId === props.userId ? 800 : 400;
};
// show comments by recent, and have the latest comment at the bottom, with the previous one just before it.
const filterComments = props.comments
.slice(0)
.sort((a, b) => {
const date1 = new Date(a.createdAt) as any;
const date2 = new Date(b.createdAt) as any;
return date2 - date1;
})
.slice(0, inc)
.reverse();
const showMoreComments = () => {
return filterComments.map((comment, i) => (
<div key={i}>
<CommentListContainer comment={comment} openModal={openModal} handleCloseModal={handleCloseModal} isBold={isBold} handleClickOpen={handleClickOpen} {...props} />
</div>
));
};
return (
<Grid>
<Fragment>
<div style={{ margin: "30px 0px" }}>
{props.comments.length > 2 ? (
<Fragment>
{min !== -1 && min !== -2 ? (
<Fragment>
{min !== 0 ? (
<OurSecondaryButton onClick={(e) => showComments(e)} component="span" color="secondary">
View {min !== -1 && min !== -2 ? min : 0} More Comments
</OurSecondaryButton>
) : (
<OurSecondaryButton onClick={(e) => showLessComments(e)} component="span" color="secondary">
Show Less Comments
</OurSecondaryButton>
)}
</Fragment>
) : (
<OurSecondaryButton onClick={(e) => showLessComments(e)} component="span" color="secondary">
Show Less Comments
</OurSecondaryButton>
)}
</Fragment>
) : null}
</div>
</Fragment>
{showLessFlag === true ? (
// will show most recent comments below
showMoreComments()
) : (
<Fragment>
{/* filter based on first comment */}
{filterComments.map((comment, i) => (
<div key={i}>
<CommentListContainer comment={comment} openModal={openModal} handleCloseModal={handleCloseModal} isBold={isBold} handleClickOpen={handleClickOpen} {...props} />
</div>
))}
</Fragment>
)}
</Grid>
);
}
// prevents un-necesary re renders
export default React.memo(CommentList);
The question seems quite complicated, but i over complicated it.
What i had to do was add this
setTimeout(() => {
divRef.current.scrollIntoView({ behavior: "smooth", block: "center", inline: "nearest" });
}, 1500);
in the commentSubmit method, this will scroll to the recent comment.
const commentSubmit = (e: any, id: number) => {
e.preventDefault();
const formData = {
comment_body,
id,
gifUrl,
};
props.postComment(formData);
setCommentBody("");
setOpenForm(false);
console.log(divRef);
// divRef.current.scrollIntoView({ behavior: "smooth" });
// my attempt to scroll to the lastest comment.
setTimeout(() => {
divRef.current.scrollIntoView({ behavior: "smooth", block: "center", inline: "nearest" });
}, 1500);
}
And make some adjustments to the code, if there are no comments divRef will throw an error upon adding a comment, because it is looking for a comment but there aren't any comments, so we will do this if there are comments less than 1 or 2. In my case im using 2 cause im showing 2 comments initially and i have a filter that shows more comments, and show less.
{post.Comments.length === 0 ? <div ref={divRef}></div> : null}
reference scrollIntoView Scrolls just too far
Rest of the code
{post.Comments.length === 0 ? <div ref={divRef}></div> : null}
{post.Comments.length > 0 ? (
<Fragment>
<Typography style={{ padding: "10px 0px", margin: "20px 0px" }}>Commments</Typography>
<CommentList ref={divRef} user={currentUser} deleteComment={props.deleteComment} userId={post.userId} postId={post.id} comments={post.Comments} {...props} />
{/* if show more hide show more button and show show less comments button */}
</Fragment>
) : (
<Grid item={true} sm={12} lg={12} style={{ padding: "30px 0px" }}>
<Typography>No Commments Yet</Typography>
</Grid>
)}
{post.Comments.length < 2 ? <div ref={divRef}></div> : null}