Comment utiliser les boutons radio dans ReactJS?

204

Je suis nouveau sur ReactJS, désolé si cela sonne. J'ai un composant qui crée plusieurs lignes de table en fonction des données reçues.

Chaque cellule de la colonne possède une case à cocher radio. Par conséquent, l'utilisateur peut sélectionner un site_nameet un addressparmi les lignes existantes. La sélection doit être indiquée dans le pied de page. Et c'est là que je suis coincé.

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

Dans jQuery, je pouvais faire quelque chose comme $("input[name=site_name]:checked").val()obtenir la sélection d'un type de case à cocher radio et l'insérer dans la première cellule de pied de page.

Mais il doit sûrement y avoir une manière Reactjs, ce qui me manque totalement? Merci beaucoup

Houman
la source
3
inputles éléments n'ont pas de contenu. Cela <input>content</input>n'a donc aucun sens et n'est pas valide. Vous voudrez peut-être <label><input />content</label>.
Oriol
1
les boutons radio ne doivent-ils pas avoir les mêmes noms avec des valeurs différentes pour fonctionner?
pgee70

Réponses:

209

Toute modification du rendu doit être modifiée via le stateou props( doc de réaction ).

Donc, ici, j'enregistre l'événement de l'entrée, puis je change le state, ce qui déclenchera ensuite le rendu pour l'afficher dans le pied de page.

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
la source
3
C'était très utile. Merci. Ce que je ne comprends pas, c'est le morceau }, this);juste en dessous du </tbody>. Qu'est-ce que c'est et que fait-il? J'ai remarqué sans que le code plante.
Houman
3
Le thisest le contexte passé à la mapfonction . C'était le montage de @FakeRainBrigand, bonne prise! Sans cela, votre fonction thisdans la carte fera référence à la window, qui n'a aucune idée de quoi il states'agit
ChinKang
8
Cette thisastuce n'est pas nécessaire si vous utilisez une flèche ES6 au lieu d'une fonction régulière. Par exemple: var resultRows = this.props.data.map((result) => { ... });je ne le mentionne que parce que les React-ers utilisent généralement déjà une forme de transpilation (pour JSX), donc ES6 est généralement à portée de main.
Tom
1
L'utilisation de deux fonctions pour obtenir les valeurs des boutons radio semble ambiguë et inutile. Vous pouvez simplement définir une fonction, peutonInputChange (e) {this.setState({ [e.target.name]: e.target.value }); }
xplorer1
110

Voici une façon simple d'implémenter des boutons radio dans 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>

Édité

Vous pouvez utiliser la fonction flèche au lieu de la liaison. Remplacez le code ci-dessus par

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

Pour une valeur par défaut, utilisez defaultCheckedcomme ceci

<input type="radio" value="MALE" defaultChecked name="gender"/> Male
Saahithyan Vigneswaran
la source
6
Je crois que .bind(this)cela créera une nouvelle fonction à chaque fois. Cela signifie que lorsque react vérifie pour voir si quelque chose a changé dans le DOM virtuel, ce composant sera toujours différent et devra toujours être rendu.
WoodenKitty
1
La nouvelle fonction sur chaque rendu ne serait un problème que si vous transmettiez cette fonction comme accessoire à un composant enfant. Dans ce cas, le composant enfant recevant les accessoires peut être rendu inutilement. Le composant parent serait bien.
Mark McKelvy
5
Pour moi, cela ne fonctionne qu'une seule fois en cliquant sur chaque radio qui partage le même nom. Par exemple, j'ai deux radios qui ont le même nameaccessoire. Ils sont tous les deux enveloppés dans un div qui a le onChangegestionnaire comme décrit dans cette réponse. Je clique sur la première radio, un événement est déclenché. Je clique sur la deuxième radio, un événement est déclenché. Cependant, la troisième fois et après, aucun événement n'est déclenché. Pourquoi?
Squrler
4
@Saahithyan J'avais le même attribut de nom attaché à toutes les radios. Il s'est avéré que je devais mettre un gestionnaire onClick aux entrées radio au lieu d'un gestionnaire onChange. Cela a fait l'affaire.
Squrler
3
Je suis d'accord avec @Squrler - avec onChange, la poignée ne se déclenche qu'une seule fois pour chacun des boutons radio inclus dans le div. Avec onClick, cela fonctionne plusieurs fois. Je ne sais pas pourquoi cela est comparé à votre extrait ...
JESii
25

D'après ce que React Docs dit :

Gestion de plusieurs entrées. Lorsque vous devez gérer plusieurs éléments d'entrée contrôlés, vous pouvez ajouter un attribut de nom à chaque élément et laisser la fonction de gestionnaire choisir quoi faire en fonction de la valeur de event.target.name.

Par exemple:

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

Lien vers l'exemple: https://codesandbox.io/s/6l6v9p0qkr

Au début, aucun des boutons radio n'est sélectionné, tout this.statecomme un objet vide, mais chaque fois que le bouton radio est sélectionné this.stateobtient une nouvelle propriété avec le nom de l'entrée et sa valeur. Il est ensuite plus facile de vérifier si l'utilisateur a sélectionné un bouton radio comme:

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

ÉDITER:

Avec la version 16.7-alpha de React, il y a une proposition pour quelque chose appelé hooksqui vous permettra de faire ce genre de choses plus facilement:

Dans l'exemple ci-dessous, il y a deux groupes de boutons radio dans un composant fonctionnel. Pourtant, ils ont des entrées contrôlées:

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

Exemple de travail: https://codesandbox.io/s/6l6v9p0qkr

Tomasz Mularczyk
la source
20

Transformez le composant radio en composant stupide et passez les accessoires au 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;
Khalid Azam
la source
2
il est facile à tester car c'est un composant de vidage est une fonction pure.
Khalid Azam
7

Juste une idée ici: en ce qui concerne les entrées radio dans React, je les rend généralement toutes d'une manière différente, comme cela a été mentionné dans les réponses précédentes.

Si cela peut aider quiconque a besoin de rendre de nombreux boutons 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
la source
1
C'est bien. Très SEC et rend les choses agréables. J'utiliserais un objet pour chaque option, mais c'est une question de préférence, je suppose.
nbkhope
@nbkhope si je devais le réécrire, j'utiliserais plutôt un objet! :)
Arnaud
Cochez cette ligne: checked={ this.state.gender === value }. Les composants fonctionnels n'en ont pas this.
Abraham Hernandez
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
la source
1
Votre code fonctionne mais vous devriez aussi l'expliquer un peu
Rahul Sharma
REMARQUE : N'UTILISEZ PAS preventDefault dans le gestionnaire onChange car cela empêche de définir le paramètre vérifié sur le composant DOM
AlexNikonov
2

Un clic sur un bouton radio doit déclencher un événement qui:

  1. appelle setState, si vous souhaitez uniquement que les connaissances de sélection soient locales, ou
  2. appelle un rappel passé d'en haut self.props.selectionChanged(...)

Dans le premier cas, le changement d'état déclenchera un nouveau rendu et vous pouvez faire
<td>chosen site name {this.state.chosenSiteName} </td>

dans le second cas, la source du rappel mettra à jour les choses pour s'assurer qu'en bas de la ligne, votre instance SearchResult aura choisiSiteName et choseAddress définie dans ses accessoires.

z5h
la source
2

Pour s'appuyer sur ChinKang dit pour sa réponse, j'ai une approche plus sèche et en es6 pour ceux qui sont intéressés:

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

sauf que celui-ci aurait une valeur vérifiée par défaut.

Tony Tai Nguyen
la source
1

J'ai également été confus dans la radio, la mise en œuvre de la case à cocher. Ce dont nous avons besoin est d'écouter l'événement de changement de la radio, puis de définir l'état. J'ai fait un petit exemple de sélection de genre.

/*
 * 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
la source
0

Les gars de Bootstrap, nous le faisons comme ceci:


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
la source