React making onClick event specific for each element - javascript

I want to add a border when the class is "transporation active" but when I click one of the icons, all of them become active. I want only the clicked one to become active. This is for selecting categories. My react code is like this:
const [transportationClick, setTransportationClick] = useState(false);
const handleTransportationClick = () => setTransportationClick(!transportationClick);
/* where I use it */
<div className="transportation-types">
<div className={transportationClick ? "transportation active" : "transportation"} onClick={handleTransportationClick}>
<div className="transportation-icon">
<FaCar />
</div>
</div>
<div className={transportationClick ? "transportation active" : "transportation"} onClick={handleTransportationClick}>
<div className="transportation-icon">
<FaBus />
</div>
</div>
<div className={transportationClick ? "transportation active" : "transportation"} onClick={handleTransportationClick}>
<div className="transportation-icon">
<FaWalking />
</div>
</div>
</div>

The issue is that all the icons share the same state, which results in all of the icons becoming active when one of them is clicked. There are several ways to solve this - you can refactor each icon into it's own component:
const Icon = ({ icon }) => {
const [transportationClick, setTransportationClick] = useState(false);
const handleTransportationClick = () => setTransportationClick(!transportationClick);
return (
<div className={transportationClick ? "transportation active" : "transportation"} onClick={handleTransportationClick}>
<div className="transportation-icon">
{icon}
</div>
</div>
)
}
const Parent = () => {
return (
<div className="transportation-types">
<Icon icon={<FaCar />} />
<Icon icon={<FaBus />} />
<Icon icon={<FaWalking />} />
</div>
)
}
Or set the state to an index corresponding to the order your showing the icon in:
const Parent = () => {
const [transportationClick, setTransportationClick] = useState(0)
return (
<div className="transportation-types">
<div
className={transportationClick === 0 ? 'transportation active' : 'transportation'}
onClick={() => setTransportationClick(0)}
>
<div className="transportation-icon">
<FaCar />
</div>
</div>
<div
className={transportationClick === 1 ? 'transportation active' : 'transportation'}
onClick={() => setTransportationClick(1)}
>
<div className="transportation-icon">
<FaBus />
</div>
</div>
<div
className={transportationClick === 2 ? 'transportation active' : 'transportation'}
onClick={() => setTransportationClick(2)}
>
<div className="transportation-icon">
<FaWalking />
</div>
</div>
</div>
)
}

Related

How do I use Toggle method in Reactjs ? I have added sandbox link at the bottom

In this code, I want to toggle active class to icons div, I watched some youtube videos and they showed this method which is not what I want, this method does not add activeclass to a particular div it adds active class to every div that has icons class. I want whenever someone click onto the div active class should be added into it and when clicked again that class should get removed.
export default function Site() {
const [notActive, setActive] = useState(false);
const switchActive = () => {
notActive ? setActive(false) : setActive(true);
};
return (
<div id="container">
<nav id="navbar">
<a href="j" className="link">
Buzzify
</a>
<a href="f">
<FaRegUserCircle className="user-icon" />
</a>
</nav>
<div id="playlist-container">
<div id="playlist">
<div
className={notActive ? "icons active" : "icons"}
onClick={switchActive}
>
<BsCloudRain />
<VolSlider />
</div>
<div
className={notActive ? "icons active" : "icons"}
onClick={switchActive}
>
<GiForest />
</div>
<div className="icons">
<MdOutlineWaterDrop />
</div>
<div className="icons">
<BiWind />
</div>
<div className="icons">
<GiCampfire />
</div>
<div className="icons">
<GiThreeLeaves />
</div>
<div className="icons">
<BsMoonStars />
</div>
<div className="icons">
<BiWater />
</div>
<div className="icons">
<BiTrain />
</div>
<div className="icons">
<BiCoffeeTogo />
</div>
<div className="icons">
<FaFan />
</div>
<div className="icons">
<DiDigitalOcean />
</div>
<div className="icons">
<GiWaterfall />
</div>
<div className="icons">
<FaPlane />
</div>
<div className="icons">
<IoIosPlanet />
</div>
<div className="icons">
<GiOctopus />
</div>
</div>
</div>
</div>
);
}
CodeSandbox.io
You have two options in order to solve this:
1- You need a state for each icon that keeps track of if you clicked them or not. This could be done by making a React component that represents an icon and then use it to in your Site component:
export default function MyIcon({icon}) {
const [isActive, setIsActive] = useState(false)
return <div className={isActive ? "icons active" : "icons"}
onClick={() => {setIsActive(!isActive)}}>
{icon}
</div>
}
You can then use this component in Site() like this:
<MyIcon icon={<CertainIcon/>} />
2- You can programmatically toggle the classname "active" using the onClick event without relying on a state like this:
<div className="icons"
onClick={(event) => {event.currentTarget.classList.toggle('active')}}>
<CertainIcon/>
</div>
you should manage state per Icon like
const IconWithState = ({children}) => {
const [isActive, setIsactive] = useState(false)
const toggle = () => setIsactive(curr => !curr)
return (
<div
className={active ? "icons active" : "icons"}
onClick={toggle}
>
{children}
</div>
)
and then use it like
...
<div id="playlist-container">
<div id="playlist">
<IconWithState>
<BsCloudRain />
<VolSlider />
</IconWithState>
<IconWithState>
<GiForest />
</IconWithState>
....

React - Prevent child re-rendering in map

I have a re-render issue with a slider which appears in every element from a map.
const Attractions = () => {
const [slide, setSlide] = useState(0)
const handleSlider = (direction) => {
if(direction === "right"){
setSlide(slide + 1)
}else{
setSlide(slide - 1)
}
}
const translation = slide * - 110
return (
<div className="attractions">
<Navbar />
<Header nav={"attractions"}/>
<div className="attractionsContainer">
<div className="attractionsWrapper">
<div className="left">
</div>
<div className="right">
{attractionList.map(attraction => (
<div className="attractionWrapper" key={attraction.id}>
<div className="top">
<div className="topLeft">
<img src="https://holidaystoswitzerland.com/wp-content/uploads/2019/09/Lauterbrunnen.jpg.webp" alt="" className="avatarImg"/>
</div>
<div className="topRight">
<div className="title">{attraction.name}</div>
<div className="location"><LocationOnOutlinedIcon/>{attraction.location.city}, {attraction.location.country}</div>
</div>
<div className="moreInfo">
<MoreVertOutlinedIcon/>
</div>
</div>
<div className="middle">
<div className="sliderWrapper">
{slide > 0 &&
<div className="left" onClick={()=>handleSlider("left")}>
<ArrowBackIosNewOutlinedIcon style={{fontSize: "30px"}}/>
</div>
}
<div className="slider" style={{transform: `translateX(${translation}%)`}}>
{attraction.img.map(img =>(
<img src={img} alt="" />
))}
</div>
{slide < 2 &&
<div className="right" onClick={()=>handleSlider("right")}>
<ArrowForwardIosOutlinedIcon style={{fontSize: "30px"}}/>
</div>
}
</div>
</div>
<div className="bottom">
<div className="interactions">
<FavoriteBorderOutlinedIcon className="actionBtn"/>
<ChatBubbleOutlineOutlinedIcon className="actionBtn"/>
<CheckCircleOutlineRoundedIcon className="actionBtn"/>
<AddCircleOutlineOutlinedIcon className="actionBtn" />
</div>
<div className="description">
{attraction.description}
</div>
<div className="comments">
{attraction.comments.map(comment =>(
<div className="commentItem">
<h4>{comment.user}</h4>
<span>{comment.comment}</span>
</div>
))}
</div>
</div>
</div>
))}
</div>
</div>
</div>
<Footer />
</div>
)
}
When i get more elements from the attractionList array, whenever i click on the left or right arrow to go to the next slide, all of the sliders from the list will do the same. I figured out that i should use something so that the slide is set only based on the item from the map, but honestly i have no idea how.
If you want them to behave differently, then the attraction object should have its own slide variable and setSlide method. That way you can just put:
onclick = {() => attraction.setSlide(attraction.slide + 1)}

How to assign a classname to particular division after map in React js?

I am mapping data inside a div, all i need a border to div of "lab_add_to_cart_samp1" classname after clicking that division. I am confused how to make a division show active onclick even after mapping the data, there is an array of allabtest from API call
<div className="lab_add_to_cart_samp">
{
allabtest.map((item, index) => {
return <div className="lab_add_to_cart_samp1" key={index}>
<div className="lab_add_to_cart_samp_img1">
<img src={REACT_APP_BASE_URL+"lab-test/download/"+item.id} />
</div>
<div className="lab_add_to_cart_test_desc">
<p id="labtest_desc_txt1">{item.name}</p>
<p id="labtest_desc_txt2">What it include :</p>
<div className="labtest_desc1">
<div className="labtest_desc_detail">
<ul>
<li>Platate</li>
<li>CBC</li>
<li>HB</li>
</ul>
</div>
<div>
<ul id="lab_test_detail">
<li>PCV</li>
<li>WBC/TLC</li>
<li>WBC/TLC</li>
</ul>
</div>
<div>
<ul id="lab_test_detail">
<li>DLC</li>
<li>RBC</li>
<li>ESR</li>
</ul>
</div>
<div>
<ul id="lab_test_detail">
<li>Platelets</li>
<li>Reticilocytes</li>
<li>Blood Grouping Rh type</li>
</ul>
</div>
</div>
</div>
<div className="lab_add_to_cart_price">
<p>Rs. {item.price}</p>
<div className="lab_add_to_cart_atc">
<button onClick={()=>addtocart(item)}>
<p>Add to Cart</p>
</button>
</div>
</div>
</div>
})
}
</div>
If you want to set active for multiple divs at the same time you can do it like this
const allabtest = [
{id: 'one', name: 'one'},
{id: 'two', name: 'two'},
{id: 'three', name: 'three'},
]
function App() {
const [activeCart, setActiveCart] = useState({});
return (
<div className="lab_add_to_cart_samp">
{allabtest.map((item, index) => {
return (
<div
className={
activeCart[item.id] === true
? "lab_add_to_cart_samp1 active"
: "lab_add_to_cart_samp1"
}
onClick={() =>
setActiveCart({
...activeCart,
[item.id]: !Boolean(activeCart[item.id])
})
}
key={item.id}
>
<div>{item.name}</div>
</div>
);
})}
</div>
);
}
If you want to track only one div then
function App() {
const [activeCart, setActiveCart] = useState();
return (
<div className="lab_add_to_cart_samp">
{allabtest.map((item, index) => {
return (
<div
className={
activeCart === item.id
? "lab_add_to_cart_samp1 active"
: "lab_add_to_cart_samp1"
}
onClick={() => setActiveCart(item.id)}
key={item.id}
>
{/* Rest of your stuffs */}
</div>
);
})}
</div>
);
}

React change icon onClick from loop

I included an Icon while mapping an array and I need to change the icon on that specific iteration onclick, I also want to render the test results when the icon is clicked.
I'm not sure how to get a unique value that is specific to this iteration that will handle this change or is my structure all wrong?
** went to sleep found solution = encapsulation **
map the component so the boolean variable is local scoped to that iteration.
shared solution below
const Students = ({students}) => {
const [showTests, setShowTests] = useState(false);
return (
<div className="wrapper" key={student.id}>
<div className="img-container">
<img src={student.pic} alt="student" />
</div>
<div className="content">
<h1>
{student.firstName.toUpperCase()} {student.lastName.toUpperCase()}
</h1>
<p>Email: {student.email}</p>
<p>Company: {student.company}</p>
<p>Skill: {student.skill}</p>
<p>Average: {averageGrade(student.grades)}%</p>
{showTests && <TestResults results={student.tests} />}
</div>
{showTests ? (
<FontAwesomeIcon
onClick={() => setShowTests(!showTests)}
className="faIcon"
icon={faMinus}
/>
) : (
<FontAwesomeIcon
onClick={() => setShowTests(!showTests)}
className="faIcon"
icon={faPlus}
/>
)}
</div>
);
}
const Main = () => {
return (
<div className="main" id="main">
{filteredStudents.map(student => (
//each iteration is a unique component
<Students student={student} />
))}
</div>
);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
Thanks ahead of time for any suggestions
The return must always have a top-level tag.
const Students = ({ students }) => {
return (
<>
{students.map((student, i) => (
<div className="wrapper" key={student.id}>
<div className="img-container">
<img src={student.pic} alt="student" />
</div>
<div className="content">
<h1>
{student.firstName.toUpperCase()}{" "}
{student.lastName.toUpperCase()}
</h1>
<p>Email: {student.email}</p>
<p>Company: {student.company}</p>
<p>Skill: {student.skill}</p>
<p>Average: {averageGrade(student.grades)}%</p>
{faMinus && <TestResults results={student.tests} />}
</div>
<FontAwesomeIcon
id={i}
onClick={(e) => handleTests(e.target.id)}
className="faIcon"
icon={current ? faPlus : faMinus}
/>
</div>
))}
</>
);
};
Let me know if this is what you're thinking. Just store the index in a state variable and compare against it. When they click change the index to the one they clicked on.
const Students = ({students}) => {
const [selectedIndex, setSelectedIndex] = useState(0)
function handleTests(id, index) { //NEW
//Your current code
setSelectedIndex(index) //NEW
}
return (
{students.map((student, i) => (
<div className="wrapper" key={student.id}>
<div className="img-container">
<img src={student.pic} alt="student" />
</div>
<div className="content">
<h1>
{student.firstName.toUpperCase()} {student.lastName.toUpperCase()}
</h1>
<p>Email: {student.email}</p>
<p>Company: {student.company}</p>
<p>Skill: {student.skill}</p>
<p>Average: {averageGrade(student.grades)}%</p>
{selectedIndex == i && <TestResults results={student.tests} />} //NEW
</div>
<FontAwesomeIcon
id={i}
onClick={e => handleTests(e.target.id, i)} //NEW
className="faIcon"
icon={selectedIndex == i ? faPlus : faMinus}
/>
</div>
))}
)
}

Modal dialog displays from all elements of mapped array. How to select each item by ts and react js?

This code:
How to display a dialog when a button is clicked using react and typescript?
I wanna open dialog from each todos, how to make it ? I used react js and typescript. Help me to resolve this problem.
interface ListProps {
todos: INote[];
onDelete: (title: string) => void;
}
const TodoList: React.FunctionComponent<ListProps> = ({ todos, onDelete }) => {
const [showAlert, setShowAlert] = useState(false);
const [todo, setTodos] = useState(null);
How to select each item by ts?It doesn't work. What is reason? Thanks!
const handleOpenDialog = (todos: any) => {
setTodos(todos);
setShowAlert(true);
};
const handleCloseDialog = () => {
setShowAlert(false);
};
return (
<>
<section className="list list--wrapper">
{todos.map((todos) => (
<div className="item list__item" key={todos.title}>
<span className="item__title">{todos.title}</span>
<div className="item__group">
<input
className="item__completed"
type="checkbox"
checked={todos.completed}
/>
<span className="item__decs">{todos.desc}</span>
</div>
<div className="item__btn">
<button
className="item__btnd"
onClick={() => handleOpenDialog(todos)}
>
Delete
</button>
<button className="item__btne">Edit</button>
</div>
{showAlert && todo && (
<AlertDialog
handleCloseDialog={handleCloseDialog}
title={todos.title}
/>
)}
</div>
))}
</section>
</>
);
};
export default TodoList;
just add a condition to only show the AlertDialog on selected todos
<section className="list list--wrapper">
{todos.map((todos) => (
<div className="item list__item" key={todos.title}>
<span className="item__title">{todos.title}</span>
<div className="item__group">
<input
className="item__completed"
type="checkbox"
checked={todos.completed}
/>
<span className="item__decs">{todos.desc}</span>
</div>
<div className="item__btn">
<button
className="item__btnd"
onClick={() => handleOpenDialog(todos)}
>
Delete
</button>
<button className="item__btne">Edit</button>
</div>
{showAlert && todos.title===todo?.title && (
<AlertDialog
handleCloseDialog={handleCloseDialog}
title={todos.title}
/>
)}
</div>
))}
</section>
or just move the AlertDialog outside the map
<section className="list list--wrapper">
{todos.map((todos) => (
<div className="item list__item" key={todos.title}>
<span className="item__title">{todos.title}</span>
<div className="item__group">
<input
className="item__completed"
type="checkbox"
checked={todos.completed}
/>
<span className="item__decs">{todos.desc}</span>
</div>
<div className="item__btn">
<button
className="item__btnd"
onClick={() => handleOpenDialog(todos)}
>
Delete
</button>
<button className="item__btne">Edit</button>
</div>
</div>
))}
{showAlert && todo && (
<AlertDialog
handleCloseDialog={handleCloseDialog}
title={todos.title}
/>
)}
</section>

Categories

Resources