¿Cómo usar los botones de radio en ReactJS?

204

Soy nuevo en ReactJS, lo siento si esto suena mal. Tengo un componente que crea varias filas de tabla de acuerdo con los datos recibidos.

Cada celda dentro de la columna tiene una casilla de verificación de radio. Por lo tanto, el usuario puede seleccionar uno site_namey uno addressde las filas existentes. La selección se mostrará en el pie de página. Y ahí es donde estoy atrapado.

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>
       );
   }
});

En jQuery podría hacer algo como $("input[name=site_name]:checked").val()obtener la selección de un tipo de casilla de verificación de radio e insertarlo en la primera celda de pie de página.

Pero seguramente debe haber una forma de Reactjs, que me estoy perdiendo por completo. Muchas gracias

Houman
fuente
3
inputLos elementos no tienen contenido. Entonces <input>content</input>no tiene sentido y es inválido. Usted puede querer <label><input />content</label>.
Oriol
1
¿los botones de radio no tienen que tener los mismos nombres con diferentes valores para funcionar?
pgee70

Respuestas:

209

Cualquier cambio en el renderizado se debe cambiar a través de stateo props( react doc ).

Así que aquí registro el evento de la entrada, y luego cambio el state, que luego activará el render para mostrar en el pie de página.

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

ChinKang
fuente
3
Eso fue muy útil. Gracias. Lo que no entiendo es la parte }, this);justo debajo de </tbody>. ¿Qué es y para qué sirve? Noté sin eso que el código falla.
Houman
3
El thises el contexto pasado a la mapfunción . Fue la edición de @FakeRainBrigand, ¡buena captura! Sin ella, su función thisen el mapa se referirá a window, que no tiene idea de qué statese trata
ChinKang
8
Este thistruco es innecesario si usa una flecha ES6 en lugar de una función normal. Por ejemplo: var resultRows = this.props.data.map((result) => { ... });solo lo menciono porque los Reactores usualmente ya usan alguna forma de transpilación (para JSX), por lo que ES6 generalmente está al alcance de la mano.
Tom
1
El uso de dos funciones para obtener los valores de los botones de opción parece ambiguo e innecesario. Quizás podría definir una función, tal vezonInputChange (e) {this.setState({ [e.target.name]: e.target.value }); }
xplorer1
110

Aquí hay una forma más sencilla de implementar botones de radio en 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>

Editado

Puede usar la función de flecha en lugar de la encuadernación. Reemplace el código anterior como

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

Para un uso de valor predeterminado defaultChecked, como este

<input type="radio" value="MALE" defaultChecked name="gender"/> Male
Saahithyan Vigneswaran
fuente
66
Creo .bind(this)que creará una nueva función cada vez. Esto significa que cuando reaccione las comprobaciones para ver si algo ha cambiado en el DOM virtual, este componente siempre será diferente y siempre necesitará una nueva representación.
WoodenKitty
1
La nueva función en cada render solo sería un problema si pasara esta función como accesorios a un componente secundario. En ese caso, el componente hijo que recibe los accesorios podría renderizarse innecesariamente. El componente padre estaría bien.
Mark McKelvy
55
Para mí, esto solo funciona una vez para hacer clic en cada radio que comparte el mismo nombre. Por ejemplo, tengo dos radios que tienen el mismo nameaccesorio. Ambos están envueltos en un div que tiene el onChangecontrolador como se describe en esta respuesta. Hago clic en la primera radio, se dispara un evento. Hago clic en la segunda radio, se dispara un evento. La tercera vez y en adelante, sin embargo, no se dispara ningún evento. ¿Por qué?
Squrler
44
@Saahithyan Tenía el mismo atributo de nombre asociado a todas las radios Resultó que necesitaba poner un controlador onClick en las entradas de radio en lugar de un controlador onChange. Eso hizo el truco.
Squrler
3
Estoy de acuerdo con @Squrler: con onChange, el identificador solo se activa una vez para cada uno de los botones de radio que se incluyen bajo el div. Con onClick, funciona varias veces. No estoy seguro de por qué se compara con su fragmento ...
JESii
25

Según lo que dicen los documentos de React :

Manejo de múltiples entradas. Cuando necesite manejar múltiples elementos de entrada controlados, puede agregar un atributo de nombre a cada elemento y dejar que la función del controlador elija qué hacer en función del valor de event.target.name.

Por ejemplo:

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>
    );
  }
}

Enlace al ejemplo: https://codesandbox.io/s/6l6v9p0qkr

Al principio, ninguno de los botones de radio está seleccionado, por lo que this.statees un objeto vacío, pero cada vez que se selecciona el botón de radio this.stateobtiene una nueva propiedad con el nombre de la entrada y su valor. Entonces es más fácil verificar si el usuario seleccionó algún botón de radio como:

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

EDITAR:

Con la versión 16.7-alpha de React hay una propuesta para algo llamado hooksque le permitirá hacer este tipo de cosas más fácilmente:

En el siguiente ejemplo, hay dos grupos de botones de opción en un componente funcional. Aún así, tienen entradas controladas:

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];
}

Ejemplo de trabajo: https://codesandbox.io/s/6l6v9p0qkr

Tomasz Mularczyk
fuente
20

Haga que el componente de radio sea un componente tonto y pase los accesorios a padre.

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;
Khalid Azam
fuente
2
Es fácil de probar, ya que su componente de volcado es una función pura.
Khalid Azam
7

Solo una idea aquí: cuando se trata de entradas de radio en React, generalmente las represento de una manera diferente que se mencionó en las respuestas anteriores.

Si esto puede ayudar a cualquiera que necesite mostrar muchos botones de radio:

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>

Arnaud
fuente
1
Esto es genial. Muy SECO y hace las cosas bien. Usaría un objeto para cada opción, pero supongo que es una cuestión de preferencia.
nbkhope
@nbkhope si tuviera que reescribirlo, ¡también usaría object en su lugar! :)
Arnaud
Compruebe esta línea: checked={ this.state.gender === value }. Los componentes funcionales no tienen this.
Abraham Hernández el
6
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;
ABHIJEET KHIRE
fuente
1
Su código funciona, pero también debe explicarlo un poco
Rahul Sharma el
NOTA : NO use preventDefault en el controlador onChange porque evita que la configuración esté marcada en el componente DOM
AlexNikonov
2

Al hacer clic en un botón de opción, se activará un evento que:

  1. llama a setState, si solo desea que el conocimiento de selección sea local, o
  2. llama una devolución de llamada que se ha pasado desde arriba self.props.selectionChanged(...)

En el primer caso, el cambio de estado desencadenará una nueva representación y usted puede hacer
<td>chosen site name {this.state.chosenSiteName} </td>

en el segundo caso, la fuente de la devolución de llamada actualizará las cosas para garantizar que en el futuro, su instancia de SearchResult haya elegido SetSiteName y chosenAddress en sus accesorios.

z5h
fuente
2

Para construir sobre ChinKang dijo por su respuesta, tengo un enfoque más seco y en es6 para aquellos interesados:

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>
    )
  }
}

excepto que este tendría un valor predeterminado marcado.

Tony Tai Nguyen
fuente
1

También me confundí en la radio, la implementación de la casilla de verificación. Lo que necesitamos es escuchar el evento de cambio de la radio y luego configurar el estado. He hecho un pequeño ejemplo de selección de género.

/*
 * 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>

rab
fuente
0

Bootstrap chicos, lo hacemos así:


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>
        </>
    )
}
GilbertS
fuente