How to get parent div in handleChange with React - javascript

I have multiple divs that are dynamic (it depends on which filter the visitor checked). So I can not use getElementById.
I need to get the parent div to change its CSS if an input is checked.
Here is my code:
{workout?.map(x => {
return <div className='relative bg-gray-200 p-4 rounded-md my-3 mx-2' key={x.name}>
<input onClick={handleChecked} className='absolute right-0 top-0' type="checkbox" />
<div className='flex'>
<img className='w-2/6 rounded mr-5' src={`${x.path}`} alt={x.name} />
<div className='w-4/6'>
<h2 className='text-xl'>{x.name}</h2>
<p>Nombre de séries : {x.set}</p>
<p>Nombre de rép : {x.reps}</p>
{x.secondary ? <p>Muscles solicités : <br />
<span className='space-x-2'>
{x?.secondary?.map(k => {
return <span className='bg-white px-1 rounded-md' key={k}>{k}</span>
})}
</span>
</p> : null}
</div>
</div>
</div>
})}
The idea, is to add a border-2 border-teal-500 class to the parent div of the input when it is checked.
Here is my handleChecked:
function handleChecked(e) {
// code here
}
I saw that I had to use parentNode but the problem is, I can't store in a variable the current input because it is dynamic. Every item has its own input.
Any idea how I can handle this?

You shouldn't be using getElementById (or any other vanilla JS DOM method) in React anyway - store and change values in state instead. You can leverage this by making input a controlled component, and storing the checked values in an array, or in workout - then, when returning the JSX, you just need to check whether the x variable (the item being iterated over) indicates that the current input should be checked or not - which will also tell you whether the parent should have a different class.
const makeHandleChecked = (i) => (e) = {
setWorkout(
workout.map(
(w, j) => j !== i ? w : { ...w, checked: e.target.checked }
)
);
};
{workout?.map((x, i) => (
<div className={`relative bg-gray-200 p-4 rounded-md my-3 mx-2${x.checked ? ' checked' : ''}`} key={x.name}>
<input onClick={makeHandleChecked(i)} checked={x.checked} className='absolute right-0 top-0' type="checkbox" />

Related

Handling Form in Nextjs

In this code example I would like to try to do easy math (ket=name + names). The sum should be a number and displayed as “ket”. But when I type in 3 and 6 in my input fields, “ket” shows up to be 36 (and not 9).
export default function Kettenl() {
const submitContact = async (event) => {
event.preventDefault();
const names = event.target.names.value;
const name = event.target.name.value;
let ket = name + names;
alert(`So your name is ${ket}?`);
};
return (
<div className="max-w-xs my-2 overflow-hidden rounded shadow-lg">
<div className="px-6 py-4">
<div className="mb-2 text-xl font-bold">Contact us</div>
<form className="flex flex-col" onSubmit={submitContact}>
<label htmlFor="name" className="mb-2 italic">
Name
</label>
<input
className="mb-4 border-b-2"
id="name"
name="name"
type="number"
required
/>
<label htmlFor="name" className="mb-2 italic">
Names
</label>
<input
className="mb-4 border-b-2"
id="names"
name="names"
type="number"
required
/>
<button
type="submit"
className="px-4 py-2 font-bold text-white bg-blue-500 rounded-full hover:bg-blue-700"
>
Submit
</button>
</form>
</div>
</div>
);
}
That is because the values are being read as strings
'3'+'6'='36'
you should change it to this
const names = parseInt(event.target.names.value);
const name = parseInt(event.target.name.value);
Also, you are storing and accessing the values incorrectly. You should learn the basics of react before moving on to next.js.
https://reactjs.org/docs/forms.html
Read about controlled and uncontrolled inputs from the docs
So even if your app works with the aforementioned change, you shouldn't be doing it like this.
Thats because values are returned as a string by default from your target element. For inputs with type="number" you can just use the input value with e.target.names.valueAsNumber.
const submitContact = async (event) => {
event.preventDefault();
const names = event.target.names.valueAsNumber;
const name = event.target.name.valueAsNumber;
let ket = name + names;
alert(`So your name is ${ket}?`);
};

How to distinguish user clicks between different buttons?

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>
)

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>))}

React & Next js: Only render div if prop contains value

Im working through my app, but I'm stuck on an issue.
I have some data from contentful which I passed as props to my component. There is one piece of data that I only want to render if it contains any value.
This work to an extent, however, the background still shows.
<div className="text-white font-base text-xs text-center p-1.5 bg-black">
{`${mrr ? mrr : ""}`}
</div>
picture of the frontend
picture of the frontend 2
If anyone could help, that would be great.
#famouslastwords
The TopW3's answer is correct. I will try to explain the code.
The below text will be rendered only when mrr is true. It is same as executing below code:
const result = true && anything; // result = anything
or
const result = false && anything // result = false
{mrr && (
<div className="text-white font-base text-xs text-center p-1.5 bg-black">
{mrr}
</div>
)}
Try this code.
{mrr && (
<div className="text-white font-base text-xs text-center p-1.5 bg-black">
{mrr}
</div>
)}

method onChange not working in radio button

I have a form with a radio button that have to do something when the item selected changes, but it only happends when the page loads but no when I select other item
This is my code:
<div key={item} className="lg:w-1/2">
<label
className="flex items-center mx-6"
htmlFor={item.option_image.alt}
>
<input
className="w-5 h-5 mr-4"
type="radio"
id={item.option_image.alt}
value={item.option_image.alt}
name={slice.primary.title}
checked={item.option_image.alt === category}
onChange={console.log(item.option_image.alt)}
required
/>
<Image
src={item.option_image.url}
width={item.option_image.dimensions.width}
height={item.option_image.dimensions.height}
alt={item.option_image.alt}
/>
<p className="flex-1 my-auto ml-2 text-lg text-white 2xl:mr-4">
{item.option_label}
</p>
</label>
</div>
You have to create an arrow function as such:
onChange={()=>console.log(item.option_image.alt)}
What you are doing now is simply accessing the onChange method.
Notice the difference:
With onChange={(e) => onChange(e)}, you are essentially creating a new function that calls onChange method during each render.
With onChange={onChange}, you are directly accessing onChange method.
You need to do it like this:
onChange={() => console.log(item.option_image.alt)}
If you don't do it like this, it will run the console on the first load of the page, not when the radio is clicked.

Categories

Resources