Home > database >  React state update without key prop: RadioGroup
React state update without key prop: RadioGroup

Time:06-21

I'm working on a Radio Group and trying to make the radio's button checked value update upon a state update made by clicking another button. In the example below, I can only get the desired effect with the "key" prop. Is there any way to get this API working without the "key" prop:

https://stackblitz.com/edit/react-ts-vfehex?file=index.tsx

Steps to reproduce: [1] click buttons below RadioField and observe that the Radio doesn't update according to the value [2] Uncomment in the App the "key" prop on the RadioField [3] click buttons below the radiofield and observe the radiofield now updates

thank you!

CodePudding user response:

Try like this:

First, add a props inside every RadioFieldItem to pass the selected value:

      <RadioField
        // LOOK HERE
        // key={selected}
        selectedValue={selected}
      >
        <RadioFieldItem
          selected={selected}
          value="yes"
          onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
            setSelected(e.target.value)
          }
        />
        <RadioFieldItem
          selected={selected}
          value="no"
          onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
            setSelected(e.target.value)
          }
        />
        <RadioFieldItem
          selected={selected}
          value="maybe"
          onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
            setSelected(e.target.value)
          }
        />
      </RadioField>

Then, inside the RadioFieldItem component use a conditional to check if the selected value is equal than the item name:

export function RadioFieldItem({ value, isDisabled = false, onChange, selected }) {
  const [id, setId] = React.useState(generateId());
  const { activeIndex, setActiveIndex } = React.useContext(RadioFieldContext);

  const isRadioSelected = (value: string | undefined): boolean =>
    activeIndex === value;

  const handleChange = (e: React.ChangeEvent<HTMLInputElement>): void => {
    setActiveIndex &&
      setActiveIndex((prevState) => {
        onChange && onChange(e);
        return e.target.value;
      });
  };

  return (
    <div>
      <input
        type="radio"
        name={id}
        id={id}
        value={value}
        checked={selected==value}
        onChange={isDisabled ? undefined : handleChange}
      />
      <label htmlFor={id}>{capitalizeFirstLetter(value)}</label>
    </div>
  );
}

CodePudding user response:

Yes, you can get it going, but the approach you're taking isn't the best. You're effectively storing two copies of the selected value - once in the RadioFieldContext, and once in the local state.

The reason it works with they key property is that it forces the RadioField component to rerender every time the key changes, so that will internally rebuild the context object, and update the activeIndex property.

A better solution would be to only have a single source of truth - the context (RadioField). Then, change the context value with the buttons, and read the context value to see what is selected. Here's a demo using this solution.

  • Related