How to distinguish user clicks between different buttons? - javascript

I have rendered 3 plan options from an object array I get from the backend.
If the plan being rendered is
cheaper, the user's subscription then its corresponding button would say downgrade,
costlier, the button would say upgrade, if it is the same then the button would say current.
This logic for rendering is working fine.
But when users click on a button I am not able to identify which option they clicked. I need to update the handle click events based on the plan being rendered.
Currently, I am iterating through the tiers map, and for each tier, I am rendering the button with appropriate text. The function to display the text basically checks for the tiered pricing and returns upgrade or downgrade or current plan. I need a way to update the handle click just like the text.
How can I dynamically update the handle click events based on the tier being rendered?
const [confirmation, setConfirmation] = useState(false);
const handleConfirmation = () => {
setConfirmation(true);
console.log('button is clicked');
};
<div className='px-8 lg:px-24 grid grid-cols-3 gap-8'>
{tiers.map((tier) => (
<div
key={tier.title}
className='relative p-8 bg-white border border-gray-200 rounded-2xl shadow-sm flex flex-col'
>
<div className='flex-1'>
<h3 className='text-xl font-semibold text-gray-900'>{tier.title}</h3>
{tier.value ? (
<p className='absolute top-0 py-1.5 px-4 bg-grays-600 rounded-full text-xs font-semibold uppercase tracking-wide text-white transform-translate-y-1/2'>
VALUE PLAN
</p>
) : tier.recommended ? (
<p className='absolute top-0 py-1.5 px-4 bg-blue-500 rounded-full text-xs font-semibold uppercase tracking-wide text-white transform -translate-y-1/2'>
RECOMMENDED PLAN
</p>
) : (
''
)}
<Button
text={determineCTAText(tier)}
size='fullwidth'
variant='primary'
className='m-auto mt-8 text-center block'
handleClick={handleConfirmation}>
</Button>
}

You can modify the handleConfirmation function to receive a tier object and return a function instead. This will create a closure over the tier value and let you access it when the button's callback is called.
const handleConfirmation = (tier) => {
// Returns the callback function that will be called, with the specific `tier` value in scope
return () => {
setConfirmation(true);
// You can access any `tier` property here
console.log(`button for tier ${tier.title} was clicked`);
};
}
return (
<div className='px-8 lg:px-24 grid grid-cols-3 gap-8'>
{tiers.map((tier) => (
{/* Omitted rest of the JSX for simplicity */}
<Button
text={determineCTAText(tier)}
size='fullwidth'
variant='primary'
className='m-auto mt-8 text-centerblock'
handleClick={handleConfirmation(tier)}>
</Button>
}
</div>
)

Related

JS click event listener in laravel livewire component?

I'm trying to add some JS to add a click event to an element within a livewire component. The click event works as expected on first load, however as soon as I run a wire:click, the JS click events no longer work?
I can see that livewire is removing / updating the dom elements when I click the wire:click element so unsure whether this may have something to do with it.
What am I doing wrong? How should I be registering click events to livewire elements in JS and ensure they always work?
My javascript:
document.addEventListener('livewire:load', function () {
let pb_responsive_button = [...document.querySelectorAll('.js-pb-responsive')];
pb_responsive_button.map(function (btn) {
btn.addEventListener("click", function () {
console.log('click');
});
});
});
livewire:
<button wire:click="addComponentActive(true)">
<span class="font-bold text-sm">Add Page Block</span>
</button>
public function addComponentActive(bool $bool)
{
$this->add_component_active = $bool;
}
my view:
#if ($add_component_active === true)
<section class="bg-white shadow-lg rounded-sm mb-8 w-full h-[calc(100%-64px)] absolute z-50">
.....
</section>
#endif
<section class="px-4 sm:px-6 lg:px-8 py-8 w-full max-w-9xl mx-auto">
<div class="border border-gray-200 rounded">
<div class="bg-gray-200 p-5 flex justify-between">
<div class="bg-white border border-gray-200 rounded p-2">
<span class="font-bold text-sm">Homepage</span>
</div>
<div class="bg-blue-600 text-white border border-gray-200 rounded p-2">
<button wire:click="addComponentActive(true)">
<span class="font-bold text-sm">Add Page Block</span>
</button>
</div>
<div class="flex justify-between items-center gap-2">
<div class="js-pb-responsive js-pb-mobile bg-white border border-gray-200 rounded p-2">M</div>
<div class="js-pb-responsive js-pb-tablet bg-white border border-gray-200 rounded p-2">T</div>
<div class="js-pb-responsive js-pb-desktop bg-white border border-gray-200 rounded p-2">D</div>
</div>
</div>
</div>
</section>
UPDATE:
I've updated it to show and hide the blocks rather than display it in a conditional if statement which ensures the click events still work, since the element still exists on page.
<section class="{{$add_component_active === true ? 'block' : 'hidden'}}"> ... </section>
Surely there is a better way to handle this. I notice I have a livewire:load eventListener? Is there another event I should be listening to when the livewire view is reloaded to reinitise the JS events?
Any help would be much appreciated.
Livewire works hand-in-hand with Alpine.js. You should focus on Alpine if you want strictly front-end functionality https://alpinejs.dev/directives/on
<button #click="alert('Hello World!')">Say Hi</button>

State Update prevents classList.toggle()

I am making a timeslot-picker component and want to toggle the class of each individual timeslot-item so it changes color and also add the timeslot information to an array of objects. If I update the state array with an onClick function the classList.toggle function doesn't seem to work, as none of the timeslots change color. I would appreciate any help on how to combine both functionalites. This is the part of my component required for the functionality:
<ul className="w-[180px] h-[400px] overflow-y-scroll flex flex-col gap-1 pt-4 relative">
<div className="text-center ">{day.day}</div>
{timeslots.map((timeslot) => (
<li key={Math.random()} className="mt-3">
<button
onClick={(e) => {
e.currentTarget.classList.toggle('bg-violet-500');
setRequestedTimeslots([
...requestedTimeslots,
{
day: day.day,
start: timeslot.start,
end: timeslot.end,
},
]);
}}
className="px-4 py-2 bg-[#F5F5F5] rounded-xl w-full text-center"
>
{timeslot.start} - {timeslot.end}
</button>
</li>
))}
</ul>

React way of selecting elements and adding event listeners

I'm building a portfolio site with gatsby where I have a sidebar on the left with some links (Home, about, work, etc.) and on the right there's the main content which is one long strip of sections. Clicking a link simply scrolls to that section. I would like for a link to be styled differently when the user reaches that section. I've found a solution in vanilla javascript to apply active state that works, but I don't know how to do this the react way.
useEffect(() => {
const navbarlinks = document.querySelectorAll("nav a");
const navbarlinksActive = () => {
let position = window.scrollY + 200;
navbarlinks.forEach((navbarlink) => {
let section = document.querySelector(navbarlink.hash);
if (position >= section.offsetTop && position <= section.offsetTop + section.offsetHeight) {
navbarlink.classList.add("active");
} else {
navbarlink.classList.remove("active");
}
});
};
window.addEventListener("load", navbarlinksActive);
document.addEventListener("scroll", navbarlinksActive);
return () => {
window.removeEventListener("load", navbarlinksActive);
document.removeEventListener("scroll", navbarlinksActive);
};
}, []);
I'm selecting all the nav links, then looping through them, getting the hash property, which will match the id of the corresponding section which is then selected. Then the class is added or removed based on its position. This code is in the sidebar component where my links, which are gatsby Link elements, also are.
return (
<nav>
<Link
className="hover:text-clr-accent flex items-center py-2 px-4 transition"
to="#home"
>
<i className="fa-solid fa-house w-6 text-center text-lg"></i>
<span className="pl-2 text-lg">Home</span>
</Link>
<Link
className="hover:text-clr-accent flex items-center py-2 px-4 transition"
to="#resume"
>
<i className="fa-solid fa-file w-6 text-center text-lg"></i>
<span className="pl-2 text-lg">Resume</span>
</Link>
<Link
className="hover:text-clr-accent flex items-center py-2 px-4 transition"
to="#about"
>
<i className="fa-solid fa-address-card w-6 text-center text-lg"></i>
<span className="pl-2 text-lg">About me</span>
</Link>
<Link
className="hover:text-clr-accent flex items-center py-2 px-4 transition"
to="#work"
>
<i className="fa-solid fa-briefcase w-6 text-center text-lg"></i>
<span className="pl-2 text-lg">My work</span>
</Link>
</nav>
);
Instead of directly adding/removing the active class set the active link Id to state and render based on that. Otherwise react may overwrite your classes by rendering
Don't use the load event listener instead just call navbarlinksActive(), the window is already loaded by the time your effect is called.

React js :- Too many re-renders. React limits the number of renders to prevent an infinite loop

Q. i want to setstate of every answer map an arrray.
please hep me out.
Error: Too many re-renders. React limits the number of renders to prevent an infinite loop.
const CreateQuestion = () => {
const { id } = useParams();
const [quesindex, setQuesIndex] = useState(0);
const [answer, setAnswer] = useState();
const [selected, setSelected] = useState("");
const [error, setError] = useState("");
const authContext = useContext(AuthContext);
const [QuestionData, setQuestionData] = useState([]);
useEffect(
function () {
getTestSeries();
},
[id]
);
const getTestSeries = async () => {
try {
const token = authContext.token;
const response = await Api.get(`/user/getallquestions/${id}`, {
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${token}`,
},
});
if (response.status === 200) {
setQuestionData(response.data["getAllQuestions"]);
} else {
toast(response.data.message);
}
} catch (error) {
toast(error.message);
}
};
const handleSubmit = () => {};
return (
<>
<h1 className="mx-auto flex justify-center my-3 text-5xl font-bold">
Test Series
</h1>
<div>
{QuestionData.length > 0 &&
QuestionData.map(function (ObjectData, index) {
const {
questiontext,
questionImgURL,
options,
answers,
description,
} = ObjectData;
```
while setAnswer state on every loop
```
setAnswer(answer);
return (
<>
<div key={index} className="my-2 mx-5 md:col-span-2">
<div className="shadow px-10 py-2 bg-purple-100 sm:rounded-md sm:overflow-hidden">
<div className=" w-full space-y-6">
<div className="grid grid-cols-3 gap-6">
<div className="col-span-3 sm:col-span-2">
<h1 className=" flex text-lg font-medium space-x-1 text-gray-700">
<span className="font-semibold text-lg">
{index + 1} .
</span>
{questiontext}
</h1>
</div>
{questionImgURL === "" ? (
<div />
) : (
<div className="col-span-3 sm:col-span-2">
<img
alt="question img"
className="max-w-full object-cover max-h-40 border-2 border-red-500"
src={`https://api.microstudy.org/${questionImgURL}`}
></img>
</div>
)}
</div>
<div className="mt-4 space-y-2">
{options.length > 0 &&
options.map((options, index) => {
const { id, optiontext } = options;
return (
<div
className="flex items-center justify-start "
key={index}
>
<input
id={optiontext}
name="options"
type="radio"
value={id}
onChange={changehandler}
className="focus:ring-purple-500 h-4 w-4 text-purple-600 border-gray-800"
/>
<label
htmlFor={optiontext}
className="ml-3 block text-base font-medium text-gray-900"
>
{optiontext}
</label>
</div>
);
})}
</div>
</div>
</div>
</div>
</>
);
})}
<div className=" text-center my-3">
<button
type="submit"
onClick={handleSubmit}
className="inline-flex justify-center py-2 px-8 border border-transparent shadow-md text-lg font-semibold rounded-md text-gray-900 bg-purple-400 hover:bg-purple-500 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-purple-500"
>
Submit
</button>
</div>
</div>
</>
);
};
please Ignore below text
Please add some context to explain the code sections (or check that you have not incorrectly formatted all of your question as code).Please add some context to explain the code sections (or check that you have not incorrectly formatted all of your question as code).Please add some context to explain the code sections (or check that you have not incorrectly formatted all of your question as code).Please add some context to explain the code sections (or check that you have not incorrectly formatted all of your question as code).Please add some context to explain the code sections (or check that you have not incorrectly formatted all of your question as code).Please add some context to explain the code sections (or check that you have not incorrectly formatted all of your question as code).Please add some context to explain the code sections (or check that you have not incorrectly formatted all of your question as code).Please add some context to explain the code sections (or check that you have not incorrectly formatted all of your question as code).Please add some context to explain the code sections (or check that you have not incorrectly formatted all of your question as code).Please add some context to explain the code sections (or check that you have not incorrectly formatted all of your question as code).Please add some context to explain the code sections (or check that you have not incorrectly formatted all of your question as code).Please add some context to explain the code sections (or check that you have not incorrectly formatted all of your question as code).Please add some context to explain the code sections (or check that you have not incorrectly formatted all of your question as code).Please add some context to explain the code sections (or check that you have not incorrectly formatted all of your question as code).Please add some context to explain the code sections (or check that you have not incorrectly formatted all of your question as code).Please add some context to explain the code sections (or check that you have not incorrectly formatted all of your question as code).Please add some context to explain the code sections (or check that you have not incorrectly formatted all of your question as code).Please add some context to explain the code sections (or check that you have not incorrectly formatted all of your question as code).Please add some context to explain the code sections (or check that you have not incorrectly formatted all of your question as code).Please add some context to explain the code sections (or check that you have not incorrectly formatted all of your question as code).Please add some context to explain the code sections (or check that you have not incorrectly formatted all of your question as code).Please add some context to explain the code sections (or check that you have not incorrectly formatted all of your question as code).Please add some context to explain the code sections (or check that you have not incorrectly formatted all of your question as code).Please add some context to explain the code sections (or check that you have not incorrectly formatted all of your question as code).Please add some context to explain the code sections (or check that you have not incorrectly formatted all of your question as code).Please add some context to explain the code sections (or check that you have not incorrectly formatted all of your question as code).
Explanation
The error lies in the onClick prop of your button. In your current implementation,handleSubmit is being called in every render loop. To fix this, we can wrap the handleSubmit function in an arrow function, ensuring that it will only called if the button is being clicked.
Your code
<button
type="submit"
onClick={handleSubmit}
className="inline-flex justify-center py-2 px-8 border border-transparent shadow-md text-lg font-semibold rounded-md text-gray-900 bg-purple-400 hover:bg-purple-500 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-purple-500"
>
Fix
<button
type="submit"
onClick={() => handleSubmit}
className="inline-flex justify-center py-2 px-8 border border-transparent shadow-md text-lg font-semibold rounded-md text-gray-900 bg-purple-400 hover:bg-purple-500 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-purple-500"
>

take out name of dynamically created cards react

I had created some cards from an array of objects, now I want to show popup data for each card differently.
here is my code of printing
{data.carData.map( single=>(<li>{ <div onClick={handleClick}><Card single={single}/></div>} </li>))}
for this, I want the card name onClick event, but when I pass anything in the handleClick react throw an error of too many re-renders.
if I do the same onClick event inside the card component and log it prints all the cards names one by one
here is what is inside the card:
function Card({single}) {
const [flowName, setflowName] = useState();
const handleClick=(e)=>{
setflowName(e);
return
}
console.log("loging name:",flowName);
return (
<div className="main mb-2 z-0" onClick={handleClick(e=>single?.flowName)} >
<div className=" inner_main_container pt-4 px-8 bg-white shadow-lg rounded-lg ">
<div className="flex justify-between">
<div className="flex flex-row align-center">
</div>
{single?.badge.map(badge=>(
<div className={"badge"} key={badge}>
<span className={badge==="Growth"?" badgeClrG":(badge==="Content"?"content":"badgeClrO")} key={badge}>{badge}</span>
</div>
) )}
</div>
<div className="flex justify-center">
<img src={single?.image} alt={single?.flowName} />
</div>
<div >
<div className={"mt-2 flex justify-center"}>
<h1>{single?.flowName}</h1>
</div>
</div>
</div>
</div>
) } export default Card
I suspect you're doing this when you're trying to pass a value:
single=>(<li>{ <div onClick={handleClick(value)}><Card single={single}/></div>} </li>))}
Every time this is rendered, the function is invoked straight away, which causes another render and so on...
The solution is to wrap your handleClick call in another function:
single=>(<li>{ <div onClick={()=>handleClick(value)}><Card single={single}/></div>} </li>))}

Categories

Resources