Obtenga datos de formulario en ReactJS

200

Tengo una forma simple en mi renderfunción, así:

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

¿Qué debo escribir en mi handleLogin: function() { ... }para acceder Emaily Passwordcampos?

myusuf
fuente
11
@ssorallen ¿Qué tontería es esta? El autor de la pregunta podría estar construyendo un SPA y probablemente no quiera que el formulario se envíe como un formulario HTML normal. Si quisieran esto, definirían una acción y eliminarían el controlador de eventos.
developerbmw
66
Nota: debe manejar onSubmitel formulario en lugar del clic del botón; de esta manera también manejará al usuario que envía el formulario presionando Intro.
developerbmw
11
Se enviará <form>una con a <button>o <input>con type=submitcuando el usuario presione Entrar en cualquiera de los formularios <input type=text>. Si confía en un onClickbotón, el usuario debe hacer clic en el botón o enfocarlo y presionar Entrar / Barra espaciadora. El uso onSubmithabilitará ambos casos de uso. Cuando los formularios no admiten Enter para enviar, pueden sentirse rotos.
Ross Allen

Respuestas:

167

Use los changeeventos en las entradas para actualizar el estado del componente y acceder a él en 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);
}

Violín de trabajo .

Además, lea los documentos, hay una sección completa dedicada al manejo de formularios : Formularios

Anteriormente, también podía usar la mezcla de ayuda de enlace de datos bidireccional de React para lograr lo mismo, pero ahora está en desuso a favor de establecer el valor y cambiar el controlador (como se indicó anteriormente):

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

La documentación está aquí: Ayudantes de enlace bidireccionales .

jbaiter
fuente
3
Para imitar correctamente la funcionalidad de valueLink, su primer ejemplo debe establecer el valuede los elementos de entrada. Sin eso, los valores están "incontrolados" en términos de Reacción. <input ... value={this.state.password}>.
Ross Allen
10
también puede usar un onSubmit con el formulario en lugar de onClick con el botón
sai
6060
¿Por qué usar un estado para cada elemento del formulario? ¿Alguien más piensa que este es un mal patrón?
Eveevans
66
Parece que valueLink está en desuso
Matt Broekhuis
3
¿No es esto almacenar la contraseña en texto claro en el lado del cliente durante todo el tiempo? Eso no parece realmente apropiado para un campo de contraseña.
NewbiZ
166

Hay algunas formas de hacer esto:

1) Obtenga valores de una matriz de elementos de formulario por índice

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

2) Usando nombre atributo de en 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) Usar referencias

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

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

Ejemplo 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
fuente
1
Esta es una respuesta fantástica. Gracias por enumerar múltiples formas de hacer esto.
Beau Smith
La opción 2 no me funciona. No hay "elementos" de propiedad ni "nombre de usuario", aunque el nombre de mi entrada es "nombre de usuario"
Madacol
@Madacol, esto puede suceder si tienes algunas entradas con el mismo nombre
Aliaksandr Sushkevich
45

Un enfoque alternativo es utilizar el refatributo y hacer referencia a los valores con this.refs. Aquí hay un ejemplo simple:

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

Se puede encontrar más información en los documentos de React: https://facebook.github.io/react/docs/more-about-refs.html#the-ref-string-attribute

Por muchas de las razones descritas en ¿Cómo uso los botones de opción en React? Este enfoque no siempre es el mejor, pero presenta una alternativa útil en algunos casos simples.

Jamund Ferguson
fuente
1
¿Esta solución es realmente mejor y no estaba disponible en el turno de preguntas, o es una forma "fea"?
Wagner Leonardi
44
en realidad no necesitas React.findDOMNode this.refs.theInput ya es un nodo html
Fareed Alnamrouti
23

Una manera fácil de lidiar con los árbitros:

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
fuente
2
Este método algún día en el futuro quedará en desuso. Las referencias solo deben usarse con devoluciones de llamada y no con cadenas. Para más detalles: facebook.github.io/react/docs/refs-and-the-dom.html
David Labbe
¡Frio! :) Me gusta usar Object.keys()seguido para map()esto:Object.keys(this.refs).map(field => formData[field] = this.refs[field].value)
Francis Rodrigues
Sí, las referencias de cadena están en desuso. Una mejor manera de hacerlo ahora es usar la devolución de llamada onChange. Si todavía quieres usar referencias, puedes usar esta sintaxis:<input type="text" ref={(input) => { this.textInput = input; }} />
E. Fortes
I @ E.Fortes, ¿puede explicar el método de devolución de llamada onChange? Muchas gracias si quieres. Por favor, etiquétame para que me informen de tu respuesta.
Simona Adriani
una forma simple sería (lo siento, el formateador de código no funciona): la clase NameForm extiende React.Component {constructor (props) {super (props); this.state = {valor: ''}; this.handleChange = this.handleChange.bind (this); } handleChange (event) {this.setState ({value: event.target.value}); } render () {return (<form> <label> Nombre: <input type = "text" value = {this.state.value} onChange = {this.handleChange} /> </label> </form>); }}
E. Fortes
23

Agregando a la respuesta 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>
    );
  }
}

Vea este artículo medio: Cómo manejar formularios con Just React

Este método obtiene datos del formulario solo cuando se presiona el botón Enviar. Mucho más limpio IMO!

hoohoo-b
fuente
Respuesta perfecta. 👌
Michal
16

Puede cambiar el onClickcontrolador de eventos en el botón a un onSubmitcontrolador en el formulario:

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

Luego puede utilizar FormDatapara analizar el formulario (y construir un objeto JSON a partir de sus entradas si lo desea).

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
fuente
Ejecutando esto más 'console.log (usuario);' da un objeto sin valor utilizable! Alguna opinión?
M al
@MahdiRafatjah ¯ \ _ (ツ) _ / ¯ - funciona en este violín: jsfiddle.net/mschock/exvko2Lf
Michael Schock
un amigo mío me refirió a un documento de reacción y lo están haciendo así reactjs.org/docs/forms.html#controlled-components
M el
1
agradable, eso parece mucho más limpio =)
Michael Schock
13

Sugeriría el siguiente enfoque:

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
fuente
Si no está volviendo a procesar el componente, ¿cómo puede establecer el valor de cada elemento de acuerdo con el estado actual? por ejemplo, "value = {this.state. destination}". Además, no puede ejecutar validaciones de reacción en el formulario si no lo está
reenviando
13

Si todas sus entradas / área de texto tienen un nombre, puede filtrar todo desde event.target:

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

Forma totalmente incontrolada 😊 sin métodos onChange, value, defaultValue ...

Aral Roca
fuente
12

Para aquellos que no quieren usar ref y restablecer el estado con OnChangeevento, puede usar el simple controlador OnSubmit y recorrer el FormDataobjeto. Este ejemplo está usando React Hooks:

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
fuente
Gran respuesta, ¡pero un truco podría eliminar la solución var de otra manera perfecta!
Louis345
5

Además, esto también se puede usar.

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
fuente
44
Incluya una explicación de por qué esta respuesta es la forma correcta de hacerlo.
Tim Hutchison
1
Solo uso un método para cambiar la forma del texto
Yurii Kosygin
La contraseña se puede ver en reaccionar herramientas de desarrollo, ¿un pequeño problema de seguridad?
Neil
5

Dale a tus entradas una referencia como esta

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

entonces puedes acceder a él en tu identificador Iniciar sesión como soo

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

Ejemplo más claro con la destrucción de 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
fuente
4

En muchos eventos en javascript, tenemos los eventque dan un objeto que incluye qué evento sucedió y cuáles son los valores, etc.

Eso es lo que usamos con los formularios en ReactJs también ...

Entonces, en su código, establece el estado en el nuevo valor ... algo como esto:

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;

Además, este es el ejemplo de formulario en React v.16, solo como referencia para el formulario que creará en el 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
fuente
4

Yo uso así usando React Component state:

<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
fuente
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>

Imagen de salida adjunta aquí

Milan Panigrahi
fuente
Por favor proporcione una explicación también.
BlackBeard
2
Trataré de explicar esto, lo cual me pareció brillante. En la función handleSubmit () está creando un objeto (formData) en el que la clave es el atributo 'ref' de cada entrada en el formulario (la declaración 'formData [data]'), y el valor es el valor de la entrada con este atributo 'ref' (la declaración 'this.refs [data] .value'). Con 'formData [data] = this.refs [data] .value' está creando este objeto. Está diciendo, literalmente: formData ['username'] = (el valor de la entrada del nombre de usuario) y formData ['password'] = (el valor de la entrada de la contraseña) La salida será: {usuario: 'blablabla', contraseña: 'xxx '}
Simona Adriani
Gracias @SimonaAdriani por tu explicación.
Milan Panigrahi
2

Esto podría ayudar a los usuarios de 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.
fuente
1
this.submitForm.bind (this) debería ser solo: this.submitForm
rekarnar
Error: los componentes de función sin estado no pueden tener referencias.
Holms
¿Estás utilizando Meteor (v1.3)?
Joe L.
1

Para mejorar la experiencia del usuario; Cuando el usuario hace clic en el botón Enviar, puede intentar que el formulario muestre primero un mensaje de envío. Una vez que hayamos recibido una respuesta del servidor, puede actualizar el mensaje en consecuencia. Logramos esto en React encadenando estados. Ver codepen o fragmentos a continuación:

El siguiente método realiza el primer cambio de estado:

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

Tan pronto como React muestre el mensaje de envío anterior en la pantalla, llamará al método que enviará los datos del formulario al servidor: this.sendFormData (). Por simplicidad, agregué un setTimeout para imitar esto.

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

En React, el método this.setState () representa un componente con nuevas propiedades. Por lo tanto, también puede agregar algo de lógica en el método render () del componente de formulario que se comportará de manera diferente según el tipo de respuesta que obtengamos del servidor. Por ejemplo:

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
fuente
1

Si tiene múltiples ocurrencias de un nombre de elemento, entonces debe 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);
};

Tenga en cuenta que el dels en este caso es RadioNodeList, no una matriz, y no es Iterable. ForEach () es un método integrado de la clase de lista. No podrá usar un mapa () o reducir () aquí.

Jeff Lowery
fuente