Home > Mobile >  Radio Button component doesn't update default option
Radio Button component doesn't update default option

Time:01-04

Having a Radio Group reusable component which should be able to set a default option selected.

This is how the component is called:

const App = () => {
  const drinks = [
    {
      label: "Coffee",
      name: "drinks-list"
    },
    {
      label: "Tea",
      name: "drinks-list"
    },
    {
      label: "Water",
      name: "drinks-list",
      disabled: true
    }
  ];

  const [selectedValue, setSelectedValue] = useState<String>(drinks[1].label);

  function radioGroupHandler(event: React.ChangeEvent<HTMLInputElement>) {
    setSelectedValue(event.target.value);
  }

  useEffect(() => {
    console.log(selectedValue);
  }, [selectedValue]);

  return (
    <PageLayout>
      <RadioButtonGroup
        label="Select your drink:"
        options={drinks}
        onChange={radioGroupHandler}
      />
    </PageLayout>
  );
};

export default App;

There is a line that sets the state:

const [selectedValue, setSelectedValue] = useState<String>(drinks[1].label);

The expected behaviour in this case would be to check the second option (Tea) when the component is rendered but it doesn't work like that - it renders the first option. I would expect to render the first option if the code would be like this:

const [selectedValue, setSelectedValue] = useState<String>(drinks[0].label);

This is RadioButtonGroup component:

const RadioButtonGroup = ({ label, options, onChange }: IInputGroup) => {
  function renderOptions() {
    return options.map(({ label, name, disabled }: IOption, index) => {
      const shortenedOptionLabel = label.replace(/\s /g, "");
      const optionId = `radio-option-${shortenedOptionLabel}`;

      return (
        <RadioButton
          value={label}
          label={label}
          key={optionId}
          id={optionId}
          name={name}
          disabled={disabled}
          defaultChecked={index === 0}
          onChange={onChange}
        />
      );
    });
  }
  return (
    <Fieldset>
      <Legend>{label}</Legend>
      <Wrapper>{renderOptions()}</Wrapper>
    </Fieldset>
  );
};

export default RadioButtonGroup;

And here is RadioButton:

const RadioButton = ({ label, id, disabled, ...rest }: InputElementProps) => {
  return (
    <Wrapper>
      <Radio id={id} type="radio" disabled={disabled} {...rest} />
      <Label htmlFor={id} disabled={disabled}>
        <span>{label}</span>
        {/* disabled && <DisabledIcon small /> */}
      </Label>
    </Wrapper>
  );
};

export default RadioButton;

Maybe it's easier on this code sandbox: https://codesandbox.io/s/custom-radio-button-group-forked-1do5u4?file=/src/App.tsx

Any idea what is wrong and why isn't the second option checked by default when the state is set useState<String>(drinks[1].label);?

CodePudding user response:

It seems that the checked value is fixed with defaultChecked={index === 0}, perhaps this could be replaced by checked={label === selectedValue} to make this component fully controlled.

Forked live on: codesandbox

Example:

In RadioButtonGroup, include selectedValue as a prop so that the child component would know which option is selected:

<RadioButtonGroup
  label="Select your drink:"
  selectedValue={selectedValue}
  options={drinks}
  onChange={radioGroupHandler}
/>

In RadioButton, use checked instead of defaultChecked for conditional check:

<RadioButton
  value={label}
  label={label}
  key={optionId}
  id={optionId}
  name={name}
  disabled={disabled}
  //            
  • Related