Jud3v Digital
Jud3v Digital

Reputation: 311

Radio button with label in react js

Hello everyone !

Today I'm trying to make a selectable attribute like nike.com Air Force 1 product page to cart

My problem is this: Capture-d-cran-2020-10-19-12-57-35.png

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

Answers (2)

Gabriel Nadaleti
Gabriel Nadaleti

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

Kurosaki_Ishigo
Kurosaki_Ishigo

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

Related Questions