Obter dados do formulário no ReactJS

199

Eu tenho uma forma simples na minha renderfunção, assim:

render : function() {
      return (
        <form>
          <input type="text" name="email" placeholder="Email" />
          <input type="password" name="password" placeholder="Password" />
          <button type="button" onClick={this.handleLogin}>Login</button>
        </form>
      );
    },
handleLogin: function() {
   //How to access email and password here ?
}

O que devo escrever no meu handleLogin: function() { ... }para acessar Emaile Passwordcampos?

myusuf
fonte
11
@ssorallen Que bobagem é essa? O solicitante pode estar criando um SPA e provavelmente não deseja que o formulário seja enviado como um formulário HTML normal. Se eles quisessem isso, definiriam uma ação e removeriam o manipulador de eventos.
Developerbmw
6
Nota: você deve manipular onSubmito formulário em vez de clicar no botão - dessa forma, você também manipulará o usuário que enviar o formulário pressionando enter.
Developerbmw
10
A <form>com a <button>ou <input>com type=submitserá enviado quando o usuário pressionar Enter em qualquer um dos formulários <input type=text>. Se você confiar em onClickum botão, o usuário deverá clicar no botão ou focalizá-lo e pressionar Enter / barra de espaço. O uso onSubmitativará os dois casos de uso. Quando os formulários não suportam Enter para enviar, eles podem se sentir quebrados.
Ross Allen

Respostas:

166

Use os changeeventos nas entradas para atualizar o estado do componente e acessá-lo em handleLogin:

handleEmailChange: function(e) {
   this.setState({email: e.target.value});
},
handlePasswordChange: function(e) {
   this.setState({password: e.target.value});
},
render : function() {
      return (
        <form>
          <input type="text" name="email" placeholder="Email" value={this.state.email} onChange={this.handleEmailChange} />
          <input type="password" name="password" placeholder="Password" value={this.state.password} onChange={this.handlePasswordChange}/>
          <button type="button" onClick={this.handleLogin}>Login</button>
        </form>);
},
handleLogin: function() {
    console.log("EMail: " + this.state.email);
    console.log("Password: " + this.state.password);
}

Violino de trabalho .

Além disso, leia os documentos, há uma seção inteira dedicada ao tratamento de formulários: Formulários

Anteriormente, você também podia usar o mixin auxiliar de ligação de dados bidirecional do React para obter a mesma coisa, mas agora está obsoleto em favor de definir o valor e alterar o manipulador (como acima):

var ExampleForm = React.createClass({
  mixins: [React.addons.LinkedStateMixin],
  getInitialState: function() {
    return {email: '', password: ''};
  },
  handleLogin: function() {
    console.log("EMail: " + this.state.email);
    console.log("Password: " + this.state.password);
  },
  render: function() {
    return (
      <form>
        <input type="text" valueLink={this.linkState('email')} />
        <input type="password" valueLink={this.linkState('password')} />
        <button type="button" onClick={this.handleLogin}>Login</button>
      </form>
    );
  }
});

A documentação está aqui: Auxiliares de encadernação bidirecional .

jbaiter
fonte
3
Para imitar corretamente a funcionalidade de valueLink, seu primeiro exemplo deve definir os valueelementos de entrada. Sem isso, os valores são "não controlados" nos termos do React. <input ... value={this.state.password}>.
Ross Allen
10
você também pode usar um onSubmit com o formulário em vez de onClick com o botão
sai
59
Por que usar um estado para cada elemento do formulário? Alguém mais acha que esse é um padrão ruim?
eveevans 6/09/16
6
Parece que o valueLink está obsoleto
Matt Broekhuis
3
Isso não é armazenar a senha em texto não criptografado no lado do cliente o tempo todo? Isso não parece realmente apropriado para um campo de senha.
NewbiZ
166

Há algumas maneiras de fazer isto:

1) Obter valores da matriz de elementos de formulário por índice

handleSubmit = (event) => {
  event.preventDefault();
  console.log(event.target[0].value)
}

2) Usando o atributo name em html

handleSubmit = (event) => {
  event.preventDefault();
  console.log(event.target.elements.username.value) // from elements property
  console.log(event.target.username.value)          // or directly
}

<input type="text" name="username"/>

3) Usando refs

handleSubmit = (event) => {
  console.log(this.inputNode.value)
}

<input type="text" name="username" ref={node => (this.inputNode = node)}/>

Exemplo completo

class NameForm extends React.Component {
  handleSubmit = (event) => {
    event.preventDefault()
    console.log(event.target[0].value)
    console.log(event.target.elements.username.value)
    console.log(event.target.username.value)
    console.log(this.inputNode.value)
  }
  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          Name:
          <input
            type="text"
            name="username"
            ref={node => (this.inputNode = node)}
          />
        </label>
        <button type="submit">Submit</button>
      </form>
    )
  }
}
Aliaksandr Sushkevich
fonte
1
Esta é uma resposta fantástica. Obrigado por listar várias maneiras de fazer isso.
Beau Smith #
A opção 2 não funciona para mim. Não há "elementos" de propriedade nem "username", mesmo que o nome da minha entrada for "username"
Madacol
@ Madacol isso pode acontecer é que você tem algumas entradas com o mesmo nome #
Aliaksandr Sushkevich
45

Uma abordagem alternativa é usar o refatributo e referenciar os valores this.refs. Aqui está um exemplo simples:

render: function() {
    return (<form onSubmit={this.submitForm}>
        <input ref="theInput" />
    </form>);
},
submitForm: function(e) {
    e.preventDefault();
    alert(React.findDOMNode(this.refs.theInput).value);
}

Mais informações podem ser encontradas nos documentos do React: https://facebook.github.io/react/docs/more-about-refs.html#the-ref-string-attribute

Por muitos dos motivos descritos em Como uso os botões de opção no React? essa abordagem nem sempre é a melhor, mas apresenta uma alternativa útil em alguns casos simples.

Jamund Ferguson
fonte
1
Essa solução é realmente melhor e não estava disponível no momento da pergunta ou é uma maneira "feia"?
Wagner Leonardi
4
na verdade, você não precisa React.findDOMNode this.refs.theInput já um nó html
Fareed Alnamrouti
23

Uma maneira fácil de lidar com referências:

class UserInfo extends React.Component {

  constructor(props) {
    super(props);
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  handleSubmit(e) {
    e.preventDefault();
    
    const formData = {};
    for (const field in this.refs) {
      formData[field] = this.refs[field].value;
    }
    console.log('-->', formData);
  }

  render() {
    return (
        <div>
          <form onSubmit={this.handleSubmit}>
            <input ref="phone" className="phone" type='tel' name="phone"/>
            <input ref="email" className="email" type='tel' name="email"/>
            <input type="submit" value="Submit"/>
          </form>
        </div>
    );
  }
}

export default UserInfo;

E. Fortes
fonte
2
Esse método será um dia no futuro descontinuado. As referências são usadas apenas com retornos de chamada e não com strings. Para mais detalhes: facebook.github.io/react/docs/refs-and-the-dom.html
David Labbe
Legal! :) Eu gosto de usar os Object.keys()seguintes map()itens:Object.keys(this.refs).map(field => formData[field] = this.refs[field].value)
Francis Rodrigues
Sim, as referências de sequência foram descontinuadas. A melhor maneira de fazer isso agora é usar o retorno de chamada onChange. Se você ainda deseja usar refs, poderá usar esta sintaxe:<input type="text" ref={(input) => { this.textInput = input; }} />
E. Fortes
I @ E.Fortes, você pode explicar o método de retorno de chamada onChange? Muito obrigado se você quiser. Por favor, marque-me para que eu seja informado da sua resposta.
Simona Adriani
uma maneira simples seria (desculpe o formatador de código não funcionar): a classe NameForm estende React.Component {constructor (props) {super (props); this.state = {value: ''}; this.handleChange = this.handleChange.bind (this); } handleChange (evento) {this.setState ({value: event.target.value}); } render () {return (<form> <label> Nome: <tipo de entrada = "texto" valor = {this.state.value} onChange = {this.handleChange} /> </label> </form>); }
E.
22

Adicionando à resposta de Michael Schock:

class MyForm extends React.Component {
  constructor() {
    super();
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  handleSubmit(event) {
    event.preventDefault();
    const data = new FormData(event.target);

    console.log(data.get('email')); // reference by form input's `name` tag

    fetch('/api/form-submit-url', {
      method: 'POST',
      body: data,
    });
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label htmlFor="username">Enter username</label>
        <input id="username" name="username" type="text" />

        <label htmlFor="email">Enter your email</label>
        <input id="email" name="email" type="email" />

        <label htmlFor="birthdate">Enter your birth date</label>
        <input id="birthdate" name="birthdate" type="text" />

        <button>Send data!</button>
      </form>
    );
  }
}

Veja este artigo médio: Como lidar com formulários com apenas reagir

Este método obtém dados do formulário somente quando o botão Enviar é pressionado. Muito limpo IMO!

hoohoo-b
fonte
Resposta perfeita. Mic
Michal
16

Você pode alternar o onClickmanipulador de eventos no botão para um onSubmitmanipulador no formulário:

render : function() {
      return (
        <form onSubmit={this.handleLogin}>
          <input type="text" name="email" placeholder="Email" />
          <input type="password" name="password" placeholder="Password" />
          <button type="submit">Login</button>
        </form>
      );
    },

Em seguida, você pode usar FormDatapara analisar o formulário (e construir um objeto JSON a partir de suas entradas, se desejar).

handleLogin: function(e) {
   const formData = new FormData(e.target)
   const user = {}

   e.preventDefault()

   for (let entry of formData.entries()) {
       user[entry[0]] = entry[1]
   }

   // Do what you will with the user object here
}
Michael Schock
fonte
Executando isso mais 'console.log (usuário);' dá um objeto sem valor utilizável! Alguma opinião?
M em
@MahdiRafatjah ¯ \ _ (ツ) _ / ¯ - ele funciona neste violino: jsfiddle.net/mschock/exvko2Lf
Michael Schock
um amigo meu me indicou
M:
1
bom, que parece muito mais limpo =)
Michael Schock
13

Eu sugeriria a seguinte abordagem:

import {Autobind} from 'es-decorators';

export class Form extends Component {

    @Autobind
    handleChange(e) {
        this.setState({[e.target.name]: e.target.value});
    }

    @Autobind
    add(e) {
        e.preventDefault();
        this.collection.add(this.state);
        this.refs.form.reset();
    }

    shouldComponentUpdate() {
        return false;
    }

    render() {
        return (
            <form onSubmit={this.add} ref="form">
                <input type="text" name="desination" onChange={this.handleChange}/>
                <input type="date" name="startDate" onChange={this.handleChange}/>
                <input type="date" name="endDate" onChange={this.handleChange}/>
                <textarea name="description" onChange={this.handleChange}/>
                <button type="submit">Add</button>
            </form>
        )
    }

}
James Akwuh
fonte
Se você não está renderizando novamente o componente, como pode definir o valor de cada elemento de acordo com o estado atual? por exemplo, "value = {this.state. destination}". Também não é possível executar validações de reação no formulário se você não o renderizar
novamente
13

Se todas as suas entradas / área de texto tiverem um nome, você poderá filtrar todas as opções de event.target:

onSubmit(event){
  const fields = Array.prototype.slice.call(event.target)
      .filter(el => el.name)
      .reduce((form, el) => ({
        ...form,
        [el.name]: el.value,
      }), {})
}

Formulário totalmente não controlado 😊 sem métodos onChange, valor, defaultValue ...

Aral Roca
fonte
12

Para aqueles que não desejam usar ref e redefinir o estado com OnChangeevento, basta usar o identificador OnSubmit simples e percorrer o FormDataobjeto. Este exemplo está usando ganchos de reação:

const LoginPage = () =>{
    const handleSubmit = (event) => {
        const formData = new FormData(event.target);
        event.preventDefault();
        for (var [key, value] of formData.entries()) {
            console.log(key, value);
        }
    }

    return (
        <div>
        <form onSubmit={
        handleSubmit
        }

        >
        <input type="text" name="username" placeholder="Email" />
        <input type="password" name="password"
        placeholder="Password" />
        <button type="submit">Login</button>
        </form>
        </div>)
        }
CloudWave
fonte
Ótima resposta, mas um nitpick você pode remover a solução perfeita, caso contrário, var!
Louis345
5

Além disso, isso também pode ser usado.

handleChange: function(state,e) {
  this.setState({[state]: e.target.value});
},
render : function() {
  return (
    <form>
      <input type="text" name="email" placeholder="Email" value={this.state.email} onChange={this.handleChange.bind(this, 'email')} />
      <input type="password" name="password" placeholder="Password" value={this.state.password} onChange={this.handleChange.bind(this, 'password')}/>
      <button type="button" onClick={this.handleLogin}>Login</button>
    </form>
  );
},
handleLogin: function() {
  console.log("EMail: ", this.state.email);
  console.log("Password: ", this.state.password);
}
Yurii Kosygin
fonte
4
Por favor, inclua uma explicação do motivo pelo qual esta resposta está correta.
Tim Hutchison
1
Eu uso apenas um método para formulário de alteração de texto
Yurii Kosygin
A senha pode ser vista nas ferramentas react dev, um problema de segurança?
Neil
5

Dê suas entradas ref assim

<input type="text" name="email" placeholder="Email" ref="email" />
<input type="password" name="password" placeholder="Password" ref="password" />

então você pode acessá-lo em seu handleLogin como soo

handleLogin: function(e) {
   e.preventDefault();
    console.log(this.refs.email.value)
    console.log(this.refs.password.value)
}
Imran Rafique
fonte
5

Exemplo mais claro com a destruição do es6

class Form extends Component {
    constructor(props) {
        super(props);
        this.state = {
            login: null,
            password: null,
            email: null
        }
    }

    onChange(e) {
        this.setState({
            [e.target.name]: e.target.value
        })
    }

    onSubmit(e) {
        e.preventDefault();
        let login = this.state.login;
        let password = this.state.password;
        // etc
    }

    render() {
        return (
            <form onSubmit={this.onSubmit.bind(this)}>
                <input type="text" name="login" onChange={this.onChange.bind(this)} />
                <input type="password" name="password" onChange={this.onChange.bind(this)} />
                <input type="email" name="email" onChange={this.onChange.bind(this)} />
                <button type="submit">Sign Up</button>
            </form>
        );
    }
}
Sergio Ivanuzzo
fonte
4

Em muitos eventos em javascript, temos eventum objeto que inclui o evento que aconteceu e quais são os valores, etc.

É isso que usamos com os formulários no ReactJs também ...

Portanto, no seu código, você define o estado para o novo valor ... algo como isto:

class UserInfo extends React.Component {

  constructor(props) {
    super(props);
    this.handleLogin = this.handleLogin.bind(this);
  }

  handleLogin(e) {
    e.preventDefault();
    for (const field in this.refs) {
      this.setState({this.refs[field]: this.refs[field].value});
    }
  }

  render() {
    return (
        <div>
          <form onSubmit={this.handleLogin}>
            <input ref="email" type="text" name="email" placeholder="Email" />
            <input ref="password" type="password" name="password" placeholder="Password" />
            <button type="button">Login</button>
          </form>
        </div>
    );
  }
}

export default UserInfo;

Este também é o exemplo de formulário no React v.16, apenas como referência para o formulário que você criará no futuro:

class NameForm extends React.Component {
  constructor(props) {
    super(props);
    this.state = {value: ''};

    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  handleChange(event) {
    this.setState({value: event.target.value});
  }

  handleSubmit(event) {
    alert('A name was submitted: ' + this.state.value);
    event.preventDefault();
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          Name:
          <input type="text" value={this.state.value} onChange={this.handleChange} />
        </label>
        <input type="submit" value="Submit" />
      </form>
    );
  }
}
Alireza
fonte
4

Eu uso assim usando o estado React Component:

<input type="text" name='value' value={this.state.value} onChange={(e) => this.handleChange(e)} />

handleChange(e){
   this.setState({[e.target.name]: e.target.value})
}`
Adnan Boota
fonte
3
 onChange(event){
     console.log(event.target.value);
  }
  handleSubmit(event){ 
    event.preventDefault();
    const formData = {};
      for (const data in this.refs) {
        formData[data] = this.refs[data].value;
      }
    console.log(formData);
  }



 <form onSubmit={this.handleSubmit.bind(this)}>
  <input type="text" ref="username" onChange={this.onChange} className="form-control"/>
  <input type="text" ref="password" onChange={this.onChange} className="form-control"/>
  <button type="submit" className="btn-danger btn-sm">Search</button>
 </form>

Imagem de saída anexada aqui

Milan Panigrahi
fonte
Por favor, forneça uma explicação também.
Blackbeard
2
Vou tentar explicar isso, que achei brilhante. Na função handleSubmit (), você está construindo um objeto (formData) no qual a chave é o atributo 'ref' de cada entrada no formulário (a instrução 'formData [data]') e o valor é o valor da entrada com esse atributo 'ref' (a instrução 'this.refs [data] .value'). Com 'formData [data] = this.refs [data] .value' você está criando este objeto. Você está dizendo, literalmente: formData ['nome de usuário'] = (o valor da entrada do nome de usuário) e formData ['senha'] = (o valor da entrada da senha) A saída será: {usuário: 'blablabla', senha: 'xxx '}
Simona Adriani 07/02
Obrigado @SimonaAdriani pela explicação.
Milan Panigrahi
2

Isso pode ajudar os usuários do Meteor (v1.3):

render: function() {
    return (
        <form onSubmit={this.submitForm.bind(this)}>
            <input type="text" ref="email" placeholder="Email" />
            <input type="password" ref="password" placeholder="Password" />
            <button type="submit">Login</button>
        </form>
    );
},
submitForm: function(e) {
    e.preventDefault();
    console.log( this.refs.email.value );
    console.log( this.refs.password.value );
}
Joe L.
fonte
1
this.submitForm.bind (this) deve ser apenas: this.submitForm
rekarnar
Erro: Os componentes da função sem estado não podem ter refs.
holms
Você está usando o Meteor (v1.3)?
7897 Joe L.
1

Para melhorar a experiência do usuário; quando o usuário clica no botão enviar, você pode tentar fazer com que o formulário mostre primeiro uma mensagem de envio. Depois de receber uma resposta do servidor, ele pode atualizar a mensagem de acordo. Conseguimos isso no React, encadeando status. Veja codepen ou trechos abaixo:

O método a seguir faz a primeira alteração de estado:

handleSubmit(e) {
    e.preventDefault();
    this.setState({ message: 'Sending...' }, this.sendFormData);
}

Assim que o React mostrar a mensagem Enviando acima na tela, ele chamará o método que enviará os dados do formulário ao servidor: this.sendFormData (). Para simplificar, adicionei um setTimeout para imitar isso.

sendFormData() {
  var formData = {
      Title: this.refs.Title.value,
      Author: this.refs.Author.value,
      Genre: this.refs.Genre.value,
      YearReleased: this.refs.YearReleased.value};
  setTimeout(() => { 
    console.log(formData);
    this.setState({ message: 'data sent!' });
  }, 3000);
}

No React, o método this.setState () renderiza um componente com novas propriedades. Portanto, você também pode adicionar alguma lógica no método render () do componente de formulário que se comportará de maneira diferente, dependendo do tipo de resposta que obtemos do servidor. Por exemplo:

render() {
  if (this.state.responseType) {
      var classString = 'alert alert-' + this.state.type;
      var status = <div id="status" className={classString} ref="status">
                     {this.state.message}
                   </div>;
  }
return ( ...

codepen

Homam Bahrani
fonte
1

Se você tiver várias ocorrências de um nome de elemento, precisará usar forEach ().

html

  <input type="checkbox" name="delete" id="flizzit" />
  <input type="checkbox" name="delete" id="floo" />
  <input type="checkbox" name="delete" id="flum" />
  <input type="submit" value="Save"  onClick={evt => saveAction(evt)}></input>

js

const submitAction = (evt) => {
  evt.preventDefault();
  const dels = evt.target.parentElement.delete;
  const deleted = [];
  dels.forEach((d) => { if (d.checked) deleted.push(d.id); });
  window.alert(deleted.length);
};

Observe que os dels nesse caso são um RadioNodeList, não uma matriz e não são iteráveis. O forEach () é um método interno da classe list. Você não poderá usar um mapa () ou reduzir () aqui.

Jeff Lowery
fonte
1

ATUALIZAÇÃO: Agora você pode usar o Formik, um pacote externo do React que facilita o manuseio de formulários!

Aqui está o link para começar

https://jaredpalmer.com/formik

Ubaid Parvaiiz
fonte