0

I am trying to create a radio group component where the value of one of the radio options is dynamically set by an input as a label. I see these often in forms, and have created it without react previously, but am having trouble with react and the material-ui library. The passing of values seems to work from the input to the radio button and therefore to the material-ui radio group component when selected, but it un-focuses (and deselects the radio button if it was selected) after every keystroke so I have to click into the input/label (and re-check the radio button) to continue typing one char at a time.

Code below:

import {FormControl, FormControlLabel, FormLabel, Radio} from "@material-ui/core";
import RadioGroup from "@material-ui/core/RadioGroup/RadioGroup";
import React from "react";
import Input from "@material-ui/core/Input/Input";


const MUIRadioGroup = ({ classes, isSubmitting, label, name, value, onChange, controls }) => {
    return (<FormControl component="fieldset" className={classes.formControl}>
            <FormLabel component="legend">{label}</FormLabel>
            <RadioGroup
                aria-label={label}
                name={name}
                className={classes.group}
                value={value}
                onChange={onChange}
            >
                {controls.map(({ value, disabled, label, ...rest }, i) => {
                  return (<FormControlLabel
                    key={value+i}
                    value={value}
                    disabled={ disabled || isSubmitting }
                    control={ <Radio disabled={ disabled || isSubmitting }/> }
                    label={ label }
                  />)
                })}
            </RadioGroup>
        </FormControl>)
};

class Test extends React.Component {
    state = {
        value: undefined,  // so we don't default select the radio with blank input
        radioInputValue: ''
    }

    handleChange = (e) => {
        this.setState({ value: e.target.value });
    };

    handleRadioInputChange = (e) => {
        this.setState({ radioInputValue: e.target.value });
    };

    render() {
        const controls=[
                {value: '1', label: 'Choice 1', disabled: false},
                {value: '2', label: 'Choice 2', disabled: false},
                {
                    value: this.state.radioInputValue,
                    label: <Input
                        id={'Ga-radio-input'}
                        key={'Ga-radio-input'}
                        onChange={this.handleRadioInputChange}
                        name={'Ga-radio-input'}
                        value={this.state.radioInputValue}
                    />,
                    disabled: false}
            ];
        return <MUIRadioGroup controls={controls} value={this.state.value} onChange={this.handleChange} isSubmitting={false} label={"Choose one:"}/>
    }
}

I have a feeling this has something to do with rerendering because of stateless child components which would lead me to believe I have to track which components are focused and then pass that down as a prop. Is that the case?

Can someone please provide a simple example of the "React Way" to get this working?

1 Answer 1

2

I think you should pass props such as input value and onInputChange function to MUIRadioGroup for your Input component since it is being used as a child component.

This working code is just to give you some idea. I've assumed that you only have one Input for your RadioGroup: https://codesandbox.io/s/1z65z506zl

import {
  FormControl,
  FormControlLabel,
  FormLabel,
  Radio
} from "@material-ui/core";
import RadioGroup from "@material-ui/core/RadioGroup/RadioGroup";
import React from "react";
import ReactDOM from "react-dom";
import Input from "@material-ui/core/Input/Input";

const MUIRadioGroup = ({
  classes,
  isSubmitting,
  label,
  name,
  value,
  onChange,
  controls,
  InputVal,
  onInputChange
}) => {
  return (
    <FormControl component="fieldset">
      <FormLabel component="legend">{label}</FormLabel>
      <RadioGroup
        aria-label={label}
        name={name}
        // className={classes.group}
        value={value}
        onChange={onChange}
      >
        {controls.map(({ value, disabled, label, ...rest }, i) => {
          return (
            <FormControlLabel
              key={value + i}
              value={label ? value : InputVal}
              disabled={disabled || isSubmitting}
              control={<Radio disabled={disabled || isSubmitting} />}
              label={
                label ? (
                  label
                ) : (
                  <Input
                    id={"Ga-radio-input"}
                    key={"Ga-radio-input"}
                    onChange={onInputChange}
                    name={"Ga-radio-input"}
                    value={InputVal}
                  />
                )
              }
            />
          );
        })}
      </RadioGroup>
    </FormControl>
  );
};

class Test extends React.Component {
  state = {
    value: undefined, // so we don't default select the radio with blank input
    radioInputValue: ""
  };

  handleChange = e => {
    this.setState({ value: e.target.value }, () =>
      console.log(this.state.value)
    );
  };

  handleRadioInputChange = e => {
    this.setState({ radioInputValue: e.target.value }, () => {
      console.log(this.state.radioInputValue);
    });
  };

  render() {
    const controls = [
      { value: "1", label: "Choice 1", disabled: false },
      { value: "2", label: "Choice 2", disabled: false },
      {
        value: "",
        label: null,
        disabled: false
      }
    ];
    return (
      <MUIRadioGroup
        controls={controls}
        value={this.state.value}
        onChange={this.handleChange}
        isSubmitting={false}
        label={"Choose one:"}
        InputVal={this.state.radioInputValue}
        onInputChange={this.handleRadioInputChange}
      />
    );
  }
}

const rootElement = document.getElementById("root");
ReactDOM.render(<Test />, rootElement);
Sign up to request clarification or add additional context in comments.

4 Comments

I don't think it is working properly. Logging values in the radio group component will show you that it doesn't log a radio input value on typing into the field nor does it log a value when checking the radio input box. The reason is that you've defined values on the map function which are never passed in, the control objects do not contain onInputChange nor InputVal values. Deleting those expected inputs from the map function then points those variables to the values passed in as props to the component (which is what we wanted) and that leads to the behavior I have originally observed.
@Verbal_Kint yea I made a mistake on that. I just fixed it and now it is working properly. Other than those you've pointed out, I set the initial value to the empty string instead of this.state.radioInputValue. The child component value should not be set directly by the parent's state value. Please check out that link again.
The input field value is logged but checking that radio button does not set the radioGroup's value correctly
@Verbal_Kint I think you should be able to figure that out by yourself by looking at my code. I just fixed it for you anyway. Please check it out.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.