Reputation: 311
Hello everyone !
Today I'm trying to make a selectable attribute like nike.com Air Force 1 product page to cart
I want to select one item and store the value of the radio button in an state.
For that I'm using useRef
:
const getValueFromRadio = React.useRef(null)
For call this references I'm using onClick(myFunction)
on the label tag
<h1 className="text-center text-2xl">{product.name}</h1>
<div>
<div className={'block my-3'}>
{product.attribute !== undefined ? product.attribute.map((item, index) => <div className={'inline-block'} key={index}>
<label onClick={labelOnclickHandler} className="cursor-pointer py-2 px-3 my-3 mx-2 border border-grey-500 hover:border-black h-3 rounded" htmlFor={item.id}>{item.value}</label>
<input className="hidden" ref={getValueFromRadio} type="radio" id={item.id} value={item.value} name={'selectSize'}/>
</div>)
: null}
</div>
</div>
// sorry for the piss of shit indentation.
If I have only one attribute reference will get the S
size and the value that I store in my state is S:
const labelOnclickHandler = (e) => {
const el = e.target.classList
if (el.contains('border-grey-500')){ //toggling.
el.remove('border-grey-500')
el.add('border-black')
} else {
el.add('border-grey-500')
el.remove('border-black')
}
setPrevElement(el) // get preview el for remove the border black.
setSelectSize(getValueFromRadio.current.value) // state to cart.
}
But if I have multiple attribute, react or references return me every time "M", and i don't know if is a mistake or if is not the good way to make this.
Upvotes: 1
Views: 5058
Reputation: 1655
You can use htmlFor
on <label>
and then, when you click this label you trigger the input if the htmlFor
attribute is equal to the input ID
.
Then you can handle the state change with the radioButton's onChange
event:
<label htmlFor={item.id}>
<input
id={item.id}
type='radio'
onChange={() => radioChangeHandler(item.id)}
/>
</label>
And then:
const radioChangeHandle = (id) => {
setRadioState(id);
}
That way you could toggle your classes in a different way like this:
className={` ${ RadioState === item.id ? "border-black" : "border-grey-500" } cursor-pointer py-2 px-3`}
Upvotes: 2
Reputation: 86
Other solution is to pass the index of selected item:
const labelOnclickHandler = (e, index) => {
const el = e.target.classList
if (el.contains('border-grey-500')){ //toggling.
el.remove('border-grey-500')
el.add('border-black')
} else {
el.add('border-grey-500')
el.remove('border-black')
}
setPrevElement(el) // get preview el for remove the border black.
setSelectSize(product.attribute[index].value) // state to cart.
}
And you don't need to use ref:
<div>
<div className={"block my-3"}>
{product.attribute !== undefined
? product.attribute.map((item, index) => (
<div className={"inline-block"} key={index}>
<label
onClick={(e)=> labelOnclickHandler(e, index)}
className="cursor-pointer py-2 px-3 my-3 mx-2 border border-grey-500 hover:border-black h-3 rounded"
htmlFor={item.id}
>
{item.value}
</label>
<input
className="hidden"
type="radio"
id={item.id}
value={item.value}
name={"selectSize"}
/>
</div>
))
: null}
</div>
</div>
I recommend you to avoid the use of index as key value (key={index}). More info here https://reactjs.org/docs/lists-and-keys.html
Maybe you can use key={`product_attribute_${index}`}
Upvotes: 2