Como usar os botões de opção no ReactJS?

204

Eu sou novo no ReactJS, desculpe se isso soa errado. Eu tenho um componente que cria várias linhas da tabela de acordo com os dados recebidos.

Cada célula da coluna possui uma caixa de seleção de rádio. Portanto, o usuário pode selecionar um site_namee um addressdas linhas existentes. A seleção deve ser mostrada no rodapé. E é aí que eu estou preso.

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

No jQuery, eu poderia fazer algo como $("input[name=site_name]:checked").val()obter a seleção de um tipo de caixa de seleção de rádio e inseri-lo na primeira célula do rodapé.

Mas certamente deve haver uma maneira Reactjs, da qual estou sentindo falta totalmente? Muito Obrigado

Houman
fonte
3
inputelementos não têm conteúdo. Portanto, <input>content</input>não faz sentido e é inválido. Você pode querer <label><input />content</label>.
Oriol
1
Os botões de opção não precisam ter os mesmos nomes com valores diferentes para funcionar?
pgee70

Respostas:

209

Quaisquer alterações na renderização devem ser alteradas por meio de stateou props( reagir doc ).

Então, aqui, registro o evento da entrada e altero o state, que acionará a renderização para mostrar no rodapé.

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
fonte
3
Isso foi muito útil. Obrigado. O que eu não entendo é o pouco }, this);logo abaixo do </tbody>. O que é isso e o que isso faz? Percebi sem que o código falha.
Houman
3
O thisé o contexto passado para a mapfunção . Foi a edição de @FakeRainBrigand, boa captura! Sem ele o seu thisna função mapa irá se referir ao window, que não tem idéia do que o stateé sobre
ChinKang
8
Esse thistruque é desnecessário se você usar uma seta ES6 em vez de uma função regular. Por exemplo: var resultRows = this.props.data.map((result) => { ... });eu apenas mencionei isso porque os Reacters geralmente já estão usando alguma forma de transpilação (para JSX); portanto, o ES6 geralmente é de fácil alcance.
Tom
1
O uso de duas funções para obter os valores dos botões de opção parece ambíguo e desnecessário. Você pode apenas definir uma função, talvezonInputChange (e) {this.setState({ [e.target.name]: e.target.value }); }
xplorer1
109

Aqui está uma maneira mais simples de implementar botões de opção em 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

Você pode usar a função de seta em vez de encadernação. Substitua o código acima como

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

Para um valor padrão defaultChecked, use como este

<input type="radio" value="MALE" defaultChecked name="gender"/> Male
Saahithyan Vigneswaran
fonte
6
Eu acredito .bind(this)que criará uma nova função sempre. Isso significa que, ao reagir, verifica se alguma coisa mudou no DOM virtual, esse componente sempre será diferente e sempre precisará ser renderizado novamente.
WoodenKitty
1
A nova função em cada renderização seria apenas um problema se passássemos essa função como adereços para um componente filho. Nesse caso, o componente filho que recebe os adereços pode renderizar desnecessariamente. O componente pai ficaria bem.
Mark McKelvy
5
Para mim, isso funciona apenas uma vez para clicar em todos os rádios que compartilham o mesmo nome. Por exemplo, eu tenho dois rádios com o mesmo namesuporte. Ambos estão envolvidos em uma div que possui o onChangemanipulador conforme descrito nesta resposta. Eu clico no primeiro rádio, um evento é disparado. Eu clico no segundo rádio, um evento é disparado. Na terceira vez em diante, no entanto, nenhum evento é disparado. Por quê?
21717 Squrler
4
@ Saahithyan Eu tinha o mesmo atributo de nome anexado a todos os rádios. Acontece que eu precisava colocar um manipulador onClick nas entradas de rádio em vez de um manipulador onChange. Isso fez o truque.
21717 Squrler
3
Concordo com o @Squrler - no onChange, o identificador é acionado apenas uma vez para cada um dos botões de opção incluídos na div. Com o onClick, ele funciona várias vezes. Não sei por que é comparado com o fragmento ...
JESii
25

Com base no que o React Docs diz :

Manipulação de múltiplas entradas. Quando você precisar manipular vários elementos de entrada controlados, poderá adicionar um atributo de nome a cada elemento e deixar a função do manipulador escolher o que fazer com base no valor de event.target.name.

Por exemplo:

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 para o exemplo: https://codesandbox.io/s/6l6v9p0qkr

Inicialmente, nenhum dos botões de opção é selecionado, assim this.statecomo um objeto vazio, mas sempre que o botão de opção é selecionado this.stateobtém uma nova propriedade com o nome da entrada e seu valor. É fácil verificar se o usuário selecionou algum botão de opção como:

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

EDITAR:

Com a versão 16.7-alpha do React, existe uma proposta para algo chamado hooksque permitirá que você faça esse tipo de coisa mais fácil:

No exemplo abaixo, existem dois grupos de botões de opção em um componente funcional. Ainda assim, eles têm 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];
}

Exemplo de trabalho: https://codesandbox.io/s/6l6v9p0qkr

Tomasz Mularczyk
fonte
20

Faça o componente de rádio como componente burro e passe adereços para os pais.

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
fonte
2
é fácil testar, pois o componente de despejo é uma função pura.
Khalid Azam 11/01
7

Apenas uma idéia aqui: quando se trata de entradas de rádio no React, eu costumo renderizá-las de uma maneira diferente que foi mencionada nas respostas anteriores.

Se isso puder ajudar qualquer pessoa que precise renderizar muitos botões de opção:

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
fonte
1
Isso é ótimo. Muito seco e torna as coisas agradáveis. Eu usaria um objeto para cada opção, mas é uma questão de preferência, eu acho.
Nbkhope
@ nbkhope se eu tivesse que reescrevê-lo, usaria o objeto também! :)
Arnaud
Verifique esta linha: checked={ this.state.gender === value }. Componentes funcionais não têm 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
fonte
1
Seu código funciona, mas você também deve explicar um pouco mais
Rahul Sharma
NOTA : Não use preventDefault no manipulador onChange porque impede ajuste verificado ao componente DOM
AlexNikonov
2

Clicar em um botão de opção deve acionar um evento que:

  1. chama setState, se você deseja que o conhecimento da seleção seja local ou
  2. chama um retorno de chamada que foi passado de cima self.props.selectionChanged(...)

No primeiro caso, a mudança de estado acionará uma nova renderização e você poderá fazer
<td>chosen site name {this.state.chosenSiteName} </td>

no segundo caso, a origem do retorno de chamada atualizará as coisas para garantir que, posteriormente, sua instância SearchResult tenha escolhidoSiteName e o endereçoAddress definido em seus props.

z5h
fonte
2

Para aproveitar a resposta de ChinKang, disse que tenho uma abordagem mais seca e, em es6, para os interessados:

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

exceto que este teria um valor verificado padrão.

Tony Tai Nguyen
fonte
1

Também fiquei confuso no rádio, na implementação da caixa de seleção. O que precisamos é ouvir o evento de alteração do rádio e depois definir o estado. Fiz um pequeno exemplo de seleção 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
fonte
0

Pessoal de bootstrap, fazemos assim:


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
fonte