326

I am new to ReactJS, sorry if this sounds off. I have a component that creates several table rows according to the received data.

Each cell within the column has a radio checkbox. Hence the user can select one site_name and one address from the existing rows. The selection shall be shown in the footer. And thats where I am stuck.

var SearchResult = React.createClass({
  render: function () {
    var resultRows = this.props.data.map(function (result) {
      return (
        <tbody>
          <tr>
            <td>
              <input type="radio" name="site_name" value={result.SITE_NAME}>
                {result.SITE_NAME}
              </input>
            </td>
            <td>
              <input type="radio" name="address" value={result.ADDRESS}>
                {result.ADDRESS}
              </input>
            </td>
          </tr>
        </tbody>
      );
    });
    return (
      <table className="table">
        <thead>
          <tr>
            <th>Name</th>
            <th>Address</th>
          </tr>
        </thead>
        {resultRows}
        <tfoot>
          <tr>
            <td>chosen site name ???? </td>
            <td>chosen address ????? </td>
          </tr>
        </tfoot>
      </table>
    );
  },
});

In jQuery I could do something like $("input[name=site_name]:checked").val() to get the selection of one radio checkbox type and insert it into the first footer cell.

But surely there must be a Reactjs way, which I am totally missing? Many Thanks

2
  • 5
    input elements have no content. So <input>content</input> makes no sense and is invalid. You may want <label><input />content</label>. Commented Jan 5, 2015 at 17:15
  • 4
    don't radio buttons have to have the same names with different values to work? Commented Nov 10, 2016 at 3:45

13 Answers 13

304

Any changes to the rendering should be change via the state or props (react doc).

So here I register the event of the input, and then change the state, which will then trigger the render to show on the footer.

var SearchResult = React.createClass({
  getInitialState: function () {
    return {
      site: '',
      address: '',
    };
  },
  onSiteChanged: function (e) {
    this.setState({
      site: e.currentTarget.value,
    });
  },

  onAddressChanged: function (e) {
    this.setState({
      address: e.currentTarget.value,
    });
  },

  render: function () {
    var resultRows = this.props.data.map(function (result) {
      return (
        <tbody>
          <tr>
            <td>
              <input
                type="radio"
                name="site_name"
                value={result.SITE_NAME}
                checked={this.state.site === result.SITE_NAME}
                onChange={this.onSiteChanged}
              />
              {result.SITE_NAME}
            </td>
            <td>
              <input
                type="radio"
                name="address"
                value={result.ADDRESS}
                checked={this.state.address === result.ADDRESS}
                onChange={this.onAddressChanged}
              />
              {result.ADDRESS}
            </td>
          </tr>
        </tbody>
      );
    }, this);
    return (
      <table className="table">
        <thead>
          <tr>
            <th>Name</th>
            <th>Address</th>
          </tr>
        </thead>
        {resultRows}
        <tfoot>
          <tr>
            <td>chosen site name {this.state.site} </td>
            <td>chosen address {this.state.address} </td>
          </tr>
        </tfoot>
      </table>
    );
  },
});

jsbin

Sign up to request clarification or add additional context in comments.

4 Comments

That was very useful. Thanks. What I don't understand is the bit }, this); just underneath the </tbody>. What is it and what does it do? I noticed without that the code crashes.
The this is the context passed to the map function. It was the edit by @FakeRainBrigand, good catch! Without it your this in the map function will refer to the window, which has no idea what the state is about
This this trick is unnecessary if you use an ES6 arrow instead of a regular function. E.g.: var resultRows = this.props.data.map((result) => { ... }); I only mention it because React-ers are usually already using some form of transpilation (for JSX), so ES6 is typically within easy reach.
Using two functions to get the values of the radio buttons appears ambiguous and unnecessary. You could just define one function, perhaps onInputChange (e) {this.setState({ [e.target.name]: e.target.value }); }
180

Here is the simplest way of implementing radio buttons in react js.

class App extends React.Component {
  
  setGender(event) {
    console.log(event.target.value);
  }
  
  render() {
    return ( 
      <div onChange={this.setGender.bind(this)}>
        <input type="radio" value="MALE" name="gender"/> Male
        <input type="radio" value="FEMALE" name="gender"/> Female
      </div>
     )
  }
}

ReactDOM.render(<App/>, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>

Edited

You can use arrow function instead of binding. Replace the above code as

<div onChange={event => this.setGender(event)}>

For a default value use defaultChecked, like this

<input type="radio" value="MALE" defaultChecked name="gender"/> Male

13 Comments

I believe .bind(this) will create a new function every time. This means when react checks to see if anything has changed in the virtual DOM, this component will always be different, and always need re-rendering.
The new function on each render would only be a problem if were to pass this function down as props to a child component. In that case the child component receiving the props might render unnecessarily. The parent component would be fine.
For me this only works once for clicking every radio that shares the same name. For example, I have two radio's that have the same name prop. They're both wrapped in a div that has the onChange handler as described in this answer. I click the first radio, an event is fired. I click the second radio, an event is fired. The third time and onwards however, no event is fired. Why?
@Saahithyan I had the same name attribute attached to all radios. Turned out I needed to put an onClick handler to the radio inputs instead of an onChange handler. That did the trick.
I agree with @Squrler -- with onChange, the handle only fires once for each of the radio buttons that are included under the div. With onClick, it works multiple times. Not sure why that is compared to your snippet...
|
46

Based on what React Docs say:

Handling Multiple Inputs. When you need to handle multiple controlled input elements, you can add a name attribute to each element and let the handler function choose what to do based on the value of event.target.name.

For example:

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {};
  }

  handleChange = e => {
    const { name, value } = e.target;

    this.setState({
      [name]: value
    });
  };

  render() {
    return (
      <div className="radio-buttons">
        Windows
        <input
          id="windows"
          value="windows"
          name="platform"
          type="radio"
          onChange={this.handleChange}
        />
        Mac
        <input
          id="mac"
          value="mac"
          name="platform"
          type="radio"
          onChange={this.handleChange}
        />
        Linux
        <input
          id="linux"
          value="linux"
          name="platform"
          type="radio"
          onChange={this.handleChange}
        />
      </div>
    );
  }
}

Link to example: https://codesandbox.io/s/6l6v9p0qkr

At first, none of the radio buttons is selected so this.state is an empty object, but whenever the radio button is selected this.state gets a new property with the name of the input and its value. It eases then to check whether user selected any radio-button like:

const isSelected = this.state.platform ? true : false;

EDIT:

With version 16.7-alpha of React there is a proposal for something called hooks which will let you do this kind of stuff easier:

In the example below there are two groups of radio-buttons in a functional component. Still, they have controlled inputs:

function App() {
  const [platformValue, plaftormInputProps] = useRadioButtons("platform");
  const [genderValue, genderInputProps] = useRadioButtons("gender");
  return (
    <div>
      <form>
        <fieldset>
          Windows
          <input
            value="windows"
            checked={platformValue === "windows"}
            {...plaftormInputProps}
          />
          Mac
          <input
            value="mac"
            checked={platformValue === "mac"}
            {...plaftormInputProps}
          />
          Linux
          <input
            value="linux"
            checked={platformValue === "linux"}
            {...plaftormInputProps}
          />
        </fieldset>
        <fieldset>
          Male
          <input
            value="male"
            checked={genderValue === "male"}
            {...genderInputProps}
          />
          Female
          <input
            value="female"
            checked={genderValue === "female"}
            {...genderInputProps}
          />
        </fieldset>
      </form>
    </div>
  );
}

function useRadioButtons(name) {
  const [value, setState] = useState(null);

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

  const inputProps = {
    name,
    type: "radio",
    onChange: handleChange
  };

  return [value, inputProps];
}

Working example: https://codesandbox.io/s/6l6v9p0qkr

Comments

28

Make the radio component as dumb component and pass props to from parent.

import React from "react";

const Radiocomponent = ({ value, setGender }) => ( 
  <div onChange={setGender.bind(this)}>
    <input type="radio" value="MALE" name="gender" defaultChecked={value ==="MALE"} /> Male
    <input type="radio" value="FEMALE" name="gender" defaultChecked={value ==="FEMALE"}/> Female
  </div>
);

export default Radiocomponent;

It's easy to test as it is a dumb component (a pure function).

2 Comments

Should probably be using "checked". I'm pretty sure this is incorrect use of "defaultChecked"...
Using defaultChecked is for uncontrolled react inputs. Uncontrolled inputs won't change when react changes the value of the defaultChecked prop. It probably works in this situation with defaultChecked because the entire component gets re-rendered when the value prop changes.
12

Just an idea here: when it comes to radio inputs in React, I usually render all of them in a different way that was mentionned in the previous answers.

If this could help anyone who needs to render plenty of radio buttons:

import React from "react"
import ReactDOM from "react-dom"

// This Component should obviously be a class if you want it to work ;)

const RadioInputs = (props) => {
  /*
    [[Label, associated value], ...]
  */
  
  const inputs = [["Male", "M"], ["Female", "F"], ["Other", "O"]]
  
  return (
    <div>
      {
        inputs.map(([text, value], i) => (
	  <div key={ i }>
	    <input type="radio"
              checked={ this.state.gender === value } 
	      onChange={ /* You'll need an event function here */ } 
	      value={ value } /> 
    	    { text }
          </div>
        ))
      }
    </div>
  )
}

ReactDOM.render(
  <RadioInputs />,
  document.getElementById("root")
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

<div id="root"></div>

4 Comments

This is great. Very DRY and makes things nice. I would use an object for each option, but it's a matter of preference, I guess.
@nbkhope if I had to rewrite it, I would use object instead too! :)
Check this line: checked={ this.state.gender === value }. Functional components don't have this.
Intead of a div in: <div key={ i }> I would set a label.
8
import React, { Component } from "react";

class RadionButtons extends Component {
  constructor(props) {
    super(props);

    this.state = {
      // gender : "" , // use this one if you don't wanna any default value for gender
      gender: "male" // we are using this state to store the value of the radio button and also use to display the active radio button
    };

    this.handleRadioChange = this.handleRadioChange.bind(this);  // we require access to the state of component so we have to bind our function 
  }

  // this function is called whenever you change the radion button 
  handleRadioChange(event) {
      // set the new value of checked radion button to state using setState function which is async funtion
    this.setState({
      gender: event.target.value
    });
  }


  render() {
    return (
      <div>
        <div check>
          <input
            type="radio"
            value="male" // this is te value which will be picked up after radio button change
            checked={this.state.gender === "male"} // when this is true it show the male radio button in checked 
            onChange={this.handleRadioChange} // whenever it changes from checked to uncheck or via-versa it goes to the handleRadioChange function
          />
          <span
           style={{ marginLeft: "5px" }} // inline style in reactjs 
          >Male</span>
        </div>
        <div check>
          <input
            type="radio"
            value="female"
            checked={this.state.gender === "female"}
            onChange={this.handleRadioChange}
          />
          <span style={{ marginLeft: "5px" }}>Female</span>
        </div>
      </div>
    );
  }
}
export default RadionButtons;

3 Comments

Your code works but you should also explain it a little
NOTE: DONT use preventDefault in onChange handler because it prevents setting checked to DOM component
I basically used this approach but I had to switch to use an onClick handler.
7

Here's what I have used. Hope this helps.
Defining variable first.

const [variableName, setVariableName] = useState("");

Then, we will need the actual radio buttons.

<input
   type="radio"
   name="variableName"
   value="variableToCheck"
   onChange={(e) =>
   setVariableName("variableToCheck")
   }
   checked={variableName === "variableToCheck"}
/>

Comments

4

@Tomasz Mularczyk mentions react hooks in his answer, but I thought I'd put in a solution I recently used that uses just the useState hook.

function Radio() {
  const [currentRadioValue, setCurrentRadioValue] = useState()

  const handleRadioChange = (e) => {
    setCurrentValue(e.target.value);
  };

  return ( 
    <>
      <div>
        <input
          id="radio-item-1"
          name="radio-item-1"
          type="radio"
          value="radio-1"
          onChange={handleRadioChange}
          checked={currentRadioValue === 'radio-1'}
        />
        <label htmlFor="radio-item-1">Radio Item 1</label>
      </div>
      <div>
        <input
          id="radio-item-2"
          name="radio-item-2"
          type="radio"
          value="radio-2"
          onChange={handleRadioChange}
          checked={currentRadioValue === 'radio-2'}
        />
        <label htmlFor="radio-item-2">
          Radio Item 1
        </label>
      </div>
    </>
  );
}

Comments

2

Clicking a radio button should trigger an event that either:

  1. calls setState, if you only want the selection knowledge to be local, or
  2. calls a callback that has been passed in from above self.props.selectionChanged(...)

In the first case, the change is state will trigger a re-render and you can do
<td>chosen site name {this.state.chosenSiteName} </td>

in the second case, the source of the callback will update things to ensure that down the line, your SearchResult instance will have chosenSiteName and chosenAddress set in it's props.

Comments

2

I also got confused in radio, checkbox implementation. What we need is, listen change event of the radio, and then set the state. I have made small example of gender selection.

/*
 * A simple React component
 */
class App extends React.Component {
  constructor(params) {
     super(params) 
     // initial gender state set from props
     this.state = {
       gender: this.props.gender
     }
     this.setGender = this.setGender.bind(this)
  }
  
  setGender(e) {
    this.setState({
      gender: e.target.value
    })
  }
  
  render() {
    const {gender} = this.state
    return  <div>
        Gender:
        <div>
          <input type="radio" checked={gender == "male"} 
onClick={this.setGender} value="male" /> Male
          <input type="radio" checked={gender == "female"} 
onClick={this.setGender} value="female"  /> Female
        </div>
        { "Select Gender: " } {gender}
      </div>;
  }
}

/*
 * Render the above component into the div#app
 */
ReactDOM.render(<App gender="male" />, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>

Comments

2

To build upon ChinKang said for his answer, I have a more dry'er approach and in es6 for those interested:

class RadioExample extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      selectedRadio: 'public'
    };
  }

  handleRadioChange = (event) => {
    this.setState({
      selectedRadio: event.currentTarget.value
    })
  };

  render() {
    return (
      <div className="radio-row">
        <div className="input-row">
          <input
            type="radio"
            name="public"
            value="public"
            checked={this.state.selectedRadio === 'public'}
            onChange={this.handleRadioChange}
          />
          <label htmlFor="public">Public</label>
        </div>
        <div className="input-row">
          <input
            type="radio"
            name="private"
            value="private"
            checked={this.state.selectedRadio === 'private'}
            onChange={this.handleRadioChange}
          />
          <label htmlFor="private">Private</label>
        </div>
      </div>
    )
  }
}

except this one would have a default checked value.

Comments

1

Bootstrap guys, we do it like this:


export default function RadioButton({ onChange, option }) {
    const handleChange = event => {
        onChange(event.target.value)
    }

    return (
        <>
            <div className="custom-control custom-radio">
                <input
                    type="radio"
                    id={ option.option }
                    name="customRadio"
                    className="custom-control-input"
                    onChange={ handleChange }
                    value = { option.id }
                    />
                    <label
                        className="custom-control-label"
                        htmlFor={ option.option }
                        >
                        { option.option }
                    </label>
            </div>
        </>
    )
}

Comments

1
import React from 'react';
import './style.css';

export default function App() {
  const [currentRadioValue, setCurrentValue] = React.useState('on');
  const handleRadioChange = value => {
    setCurrentValue(value);
  };
  return (
    <div>
      <>
        <div>
          <input
            name="radio-item-1"
            value="on"
            type="radio"
            onChange={e => setCurrentValue(e.target.value)}
            defaultChecked={currentRadioValue === 'on'}
          />
          <label htmlFor="radio-item-1">Radio Item 1</label>
          {currentRadioValue === 'on' && <div>one</div>}
        </div>
        <div>
          <input
            name="radio-item-1"
            value="off"
            type="radio"
            onChange={e => setCurrentValue(e.target.value)}
            defaultChecked={currentRadioValue === 'off'}
          />
          <label htmlFor="radio-item-2">Radio Item 2</label>
          {currentRadioValue === 'off' && <div>two</div>}
        </div>
      </>
    </div>
  );
}

working example: https://stackblitz.com/edit/react-ovnv2b

1 Comment

Please provide a detailed explanation to your answer, in order for the next user to understand your answer better. Also, provide a basic coverage of the content of your link, in case it stops working in the future.

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.