React 해제 가능한 라디오버튼 만들기

React 에서 라디오버튼을 구현하기는 쉽습니다.

다만, 라디오버튼의 특성이랄까요? 한 번 선택한 값을 다시 해제하는 것이 어렵습니다.

이를 위해서 어떤 방식으로 라디오버튼 컴포넌트를 만들면, 이것을 해제 가능할지 알아보겠습니다. 

기본 코드

				
					import React, { useState, useEffect } from "react";

const SpecialRadio = ({ data, field_id, onValueChange, options }) => {
  const [inputValue, setInputValue] = useState(
    data && data[field_id] ? data[field_id] : ""
  );

  useEffect(() => {
    if (data && data[field_id]) {
      setInputValue(data[field_id]);
    }
  }, [data, field_id]);

  const handleChange = (e) => {
    const newValue = e.target.value;
    setInputValue(newValue);
    if (onValueChange) {
      onValueChange(field_id, newValue);
    }
  };

  if (!options) {
    return null;
  }

  return (
    <div className="radio-button-space">
      {Object.entries(options).map(([key, value]) => (
        <div key={key}>
          <label className="radio-label">
            <input
              className="radio-button"
              type="radio"
              id={key}
              name={field_id}
              value={key}
              onChange={handleChange}
              checked={inputValue === key}
              style={{ display: "none" }} // Optionally hide the radio button
            />
            <div className="rad-design"></div>
            <div className="radio-text"> {value}</div>
          </label>
        </div>
      ))}
    </div>
  );
};

export default SpecialRadio;

				
			

조금 거창하게 SpecialRadio 라는 이름을 달아줬습니다.

해당 컴포넌트를 Form 형식의 하위 컴포넌트로 활용할 예정입니다.

data 라는 객체에는 각각의 field_id 가 key 로 그 field_id 에 해당하는 field_value 가 Value 로 담겨져 쌍을 이룰 겁니다.

onValueChange 를 통해서 하위 컴포넌트에서 값이 변경되어도, 상위 컴포넌트로 그 변경된 값이 data 내에 반영이 될 수 있도록 합니다.

중요한 건 options 가 되겠죠?

 

				
					{0 : female, 1 : male }
				
			

options 는 이런 식으로 객체 형태로 받을 겁니다.

실제 화면에는 female 과 male이 나타날 겁니다.

그리고 누르면 0 이나 1로 실제 data 에는 담기게 되는 거죠.

이런 식으로 꾀하고 있습니다.

변경이 필요한 부분

				
					import React, { useState, useEffect } from "react";

const SpecialRadio = ({ data, field_id, onValueChange, options }) => {
  const [inputValue, setInputValue] = useState(data[field_id] || "");

  useEffect(() => {
    setInputValue(data[field_id] || "");
  }, [data, field_id]);

  const handleToggle = (key) => {
    const newValue = key === inputValue ? "" : key; // 현재 선택된 값과 같으면 빈 문자열로, 아니면 새 값으로 설정
    setInputValue(newValue);
    onValueChange(field_id, newValue);
  };

  return (
    <div className="radio-button-space">
      {Object.entries(options).map(([key, value]) => (
        <div
          key={key}
          className="radio-label"
          onClick={() => handleToggle(key)}
        >
          <div
            className={`special-design ${inputValue === key ? "selected" : ""}`}
          ></div>
          <div className="radio-text">{value}</div>
        </div>
      ))}
    </div>
  );
};

export default SpecialRadio;

				
			

저는 위와 같이 변경을 했습니다.

기본적으로 <input type=”radio” … /> 를 사용하면 이미 클릭한 버튼을 다시 한 번 클릭하는 이벤트가 발생하지 않습니다.

때문에 div 형태로 라디오버튼 컴포넌트를 변경할 필요가 있습니다. 

				
					
  /* Special Radio 에 관한 설정 */

  .special-design {
    position: absolute; /* 절대 위치 설정 */
    left: 0; /* 좌측에서 0px 떨어진 곳에 위치 */
    top: 50%; /* 상단에서 50% 떨어진 곳에 위치 */
    transform: translateY(-50%); /* Y축 기준 중앙 정렬 */
    width: 22px;
    height: 22px;
    margin-top: 0.5rem;
    border-radius: 100px;
    margin-right: 10px; /* 디자인과 텍스트 사이의 간격 */
    background: #ccc; /* 배경색 설정 */
  }
  
  .special-design::before {
    content: ''; /* 초기 상태에서는 내용 없음 */
    display: inline-block;
    width: inherit;
    height: inherit;
    line-height: 22px;
    text-align: center;
    border-radius: inherit;
    background: hsl(0, 0%, 90%);
    font-size: 14px;
    color: transparent; /* 체크 표시 색상을 숨김 */
    transition: 0.1s;
  }
  
  .special-design.selected {
    background: linear-gradient(to right bottom, hsl(330, 100%, 70%), hsl(330, 100%, 85%)); /* 배경을 다시 그리기 */
  }
  
  .special-design.selected::before {
    content: '✔'; /* 선택 상태에서 체크 표시 */
    color: white; /* 체크 표시의 색상 */
    background: linear-gradient(to right bottom, hsl(330, 100%, 70%), hsl(330, 100%, 85%)); /* 배경을 다시 그리기 */

  }

				
			

그리고 위처럼 CSS를 설정하여 예쁘게 해당 컴포넌트를 꾸며봤습니다. 

실험하기

실제로 라디오버튼의 모양을 하고 있습니다.

클릭하고 해제하는 것이 잘 되는 모습을 볼 수 있습니다. 

마치며...

이제 원하던 방식대로 동작하는 라디오버튼을 얻었습니다.