ChatGPT解决这个技术问题 Extra ChatGPT

Get form data in ReactJS

I have a simple form in my render function, like so:

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

What should I write in my handleLogin: function() { ... } to access Email and Password fields?

Note: you should handle onSubmit on the form rather than the button click - this way you will also handle the user submitting the form by pressing enter.
A <form> with a <button> or <input> with type=submit will get submitted when the user presses Enter in any of the form's <input type=text>. If you rely on an onClick of a button, the user must click the button or focus it and press Enter/Spacebar. Using onSubmit will enable both use cases. When forms don't support Enter to submit, they can feel broken.
there is also the benefit of browser's native checking on empty values of required fields

A
Aliaksandr Sushkevich

There are a few ways to do this:

1) Get values from array of form elements by index

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

2) Using name attribute in 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) Using refs

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

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

Full example

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

Option 2 doesn't work for me. There is no property "elements" nor "username", even though my input's name is "username"
@Madacol this may happen is you have a few inputs with the same name
Option 2 is the best answer. Getting the elements via getElementById is simply redundant if you already have the event param.
@Madacol, try using event.target.form.username.value. This worked for me.
S
Silicum Silium

Use the change events on the inputs to update the component's state and access it in 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);
}

Working fiddle.

Also, read the docs, there is a whole section dedicated to form handling: Forms

Previously you could also use React's two-way databinding helper mixin to achieve the same thing, but now it's deprecated in favor of setting the value and change handler (as above):

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

Documentation is here: Two-way Binding Helpers.


To correctly mimic the functionality of valueLink, your first example should set the value of the input elements. Without that, the values are "uncontrolled" in React terms. <input ... value={this.state.password}>.
you can also use an onSubmit with the form instead of onClick with the button
Why using a state for each element of the form ? Anyone else thinks this is a bad pattern ?
Looks like valueLink is deprecated
Isn't this storing the password in clear text on the client side for the whole time? That doesn't seem really appropriate for a password field.
h
hoohoo-b

Adding on to Michael Schock's answer:

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

See this Medium article: How to Handle Forms with Just React

This method gets form data only when Submit button is pressed. Much cleaner IMO!


This also prevent's re-rendering the form on continuous state changes!
C
Community

An alternative approach is to use the ref attribute and reference the values with this.refs. Here is a simple example:

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

More info can be found in the React docs: https://facebook.github.io/react/docs/more-about-refs.html#the-ref-string-attribute

For a lot of the reasons described in How do I use radio buttons in React? this approach isn't always the best, but it does present a useful alternative in some simple cases.


Does this solution is really better and wasn't available at question time, or it's a "ugly" way?
actually you don't need React.findDOMNode this.refs.theInput already an html node
refs is depreciated, this answer no longer holds.
P
Per Enström

For those who don't want to use ref and reset the state with OnChange event, you can just use simple OnSubmit handle and loop through the FormData object.

Note that you cannot access formData.entries() directly since it is an iterable, you have to loop over it.

This example is using React Hooks:

const LoginPage = () => {
  const handleSubmit = (event) => {
    const formData = new FormData(event.currentTarget);
    event.preventDefault();
    for (let [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>
  );
};

And if you're using TypeScript:

export const LoginPage: React.FC<{}> = () => {
  const handleSubmit: React.FormEventHandler<HTMLFormElement> = (event) => {
    const formData = new FormData(event.currentTarget);
    event.preventDefault();
    for (let [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>
  );
};

Great answer, but a nitpick could you remove the var otherwise perfect solution!
can anybody help me with a typescript variant of this code snippet above?
new FormData(event.target) gives {} for me
@tejasvi88 you need to call entries on the FormData object. Not all JS objects will reveal their contents upon logging.
@Joseph My form actually didn't have name attributes.
S
Sameera K

No need to use refs, you can access using event

function handleSubmit(e) {
    e.preventDefault()
    const {username, password } = e.target.elements
    console.log({username: username.value, password: password.value })
}

<form onSubmit={handleSubmit}>
   <input type="text" id="username"/>
   <input type="text" id="password"/>
   <input type="submit" value="Login" />
</form>

great, this is what I was looking for thank you
what does elements do? couldn't find anything about e.target.elements
Any way to avoid the id duplication?
@tejasvi88 you can check my method to avoid id
Great Sameera - this is easy to understand coming from vanilla js. thanks
E
E. Fortes

An easy way to deal with refs:

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 (

); } } export default UserInfo;


This method will someday in the future be deprecated. Refs are to only used with callbacks and not strings. For more details: facebook.github.io/react/docs/refs-and-the-dom.html
Cool! :) I like to use Object.keys() followed for map() like this: Object.keys(this.refs).map(field => formData[field] = this.refs[field].value)
Yes the string refs are deprecated. A better way to do it right now is to use the onChange callback. If you still want to use refs you could use this synctax: <input type="text" ref={(input) => { this.textInput = input; }} />
I @E.Fortes, can you please explain the onChange callback method? Thank you very much if you will. Please tag me so I will be informed of your answer.
a simple way would be (sorry the code formatter not working): class NameForm extends React.Component { constructor(props) { super(props); this.state = {value: ''}; this.handleChange = this.handleChange.bind(this); } handleChange(event) { this.setState({value: event.target.value}); } render() { return (
); } }
M
Michael Schock

You could switch the onClick event handler on the button to an onSubmit handler on the form:

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

Then you can make use of FormData to parse the form (and construct a JSON object from its entries if you want).

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
}

Running this plus 'console.log(user);' gives an object with no usable value ! Any opinion ?
@MahdiRafatjah ¯\_(ツ)_/¯ - it works in this fiddle: jsfiddle.net/mschock/exvko2Lf
a friend of mine referred me to react document and they are doing it like this reactjs.org/docs/forms.html#controlled-components
nice, that seems much cleaner =)
A
Aral Roca

If all your inputs / textarea have a name, then you can filter all from event.target:

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

Totally uncontrolled form 😊 without onChange methods, value, defaultValue...


J
James Akwuh

I would suggest the following approach:

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

}

If you'r not rerendering the component, how you can set the value of each element according to the current state? e.g "value={this.state. destination}". Also you can't run react validations on the form if your not rerendering it
S
Sergio Ivanuzzo

More clear example with es6 destructing

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

a
arn48

Here is shortest way to get data from form and the best way to avoid id and ref just by using FormData

import React, { Component } from 'react'

class FormComponent extends Component {
  formSubmit = (event) => {
    event.preventDefault()
    var data = new FormData(event.target)
    let formObject = Object.fromEntries(data.entries())
    console.log(formObject)
  }
  render() {
    return (
      <div>
        <form onSubmit={this.formSubmit}>
          <label>Name</label>
          <input name="name" placeholder="name" />
          <label>Email</label>
          <input type="email" name="email" />
          <input type="submit" />
        </form>
      </div>
    )
  }
}
export default FormComponent

P
Pramesh Bajracharya

Also, this can be used too.

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

Please include an explanation for why this answer is the correct way to go.
I use only one method to change text form
The password can be seen in react dev tools, bit of a security issue?
I
Imran Rafique

Give your inputs ref like this

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

then you can access it in your handleLogin like soo

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

i
ivks

If you are using Redux in your project you can consider using this higher order component https://github.com/erikras/redux-form.


A
Alireza

In many events in javascript, we have event which give an object including what event happened and what are the values, etc...

That's what we use with forms in ReactJs as well...

So in your code you set the state to the new value... something like this:

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;

Also this is the form example in React v.16, just as reference for the form you creating in the future:

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

A
Adnan Boota

I use like this using 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})
}`

M
Milan Panigrahi
 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>

Output image attached here


Please provide an explanation as well.
I'll try to explain this, which I found brilliant. In the handleSubmit() function you are building an object (formData) in which the key is the 'ref' attribute of every input in the form (the 'formData[data]' statement), and the value is the value of the input with this 'ref' attribute (the 'this.refs[data].value' statement). With 'formData[data] = this.refs[data].value' you are building this object. You are saying, literally: formData['username'] = (the value of username input) and formData['password'] = (the value of password input) Output will be: {user: 'blablabla', password: 'xxx'}
Thank you @SimonaAdriani for you explanation .
J
Joe L.

This might help Meteor (v1.3) users:

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

this.submitForm.bind(this) should be just: this.submitForm
Error: Stateless function components cannot have refs.
Are you using Meteor (v1.3) ?
H
Hom Bahrani

To improve the user experience; when the user clicks on the submit button, you can try to get the form to first show a sending message. Once we've received a response from the server, it can update the message accordingly. We achieve this in React by chaining statuses. See codepen or snippets below:

The following method makes the first state change:

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

As soon as React shows the above Sending message on screen, it will call the method that will send the form data to the server: this.sendFormData(). For simplicity I've added a setTimeout to mimic this.

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

In React, the method this.setState() renders a component with new properties. So you can also add some logic in render() method of the form component that will behave differently depending on the type of response we get from the server. For instance:

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


S
Shapon Pal

This is an example of dynamically added field. Here form data will store by input name key using React useState hook.

import React, { useState } from 'react' function AuthForm({ firebase }) { const [formData, setFormData] = useState({}); // On Form Submit const onFormSubmit = (event) => { event.preventDefault(); console.log('data', formData) // Submit here }; // get Data const getData = (key) => { return formData.hasOwnProperty(key) ? formData[key] : ''; }; // Set data const setData = (key, value) => { return setFormData({ ...formData, [key]: value }); }; console.log('firebase', firebase) return (

setData('name', e.target.value)} /> setData('email', e.target.value)} />
) } export default AuthForm


J
Jeff Lowery

If you have multiple occurrences of an element name, then you have to use 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);
};

Note the dels in this case is a RadioNodeList, not an array, and is not an Iterable. The forEach()is a built-in method of the list class. You will not be able to use a map() or reduce() here.


V
Vikrant
<form onSubmit={handleLogin}>
      <input type="text" name="email" placeholder="Email" />
      <input type="text" name="password" placeholder="Password" />
      <button type="submit">Login</button>
</form>

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

Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.
S
Sabesan

I think this is also the answer that you need. In addition, Here I add the required attributes. onChange attributes of Each input components are functions. You need to add your own logic there.

handleEmailChange: function(e) {
    this.setState({email: e.target.value});
},
handlePasswordChange: function(e) {
   this.setState({password: e.target.value});
},
formSubmit : async function(e) {
    e.preventDefault();
    // Form submit Logic
  },
render : function() {
      return (
        <form onSubmit={(e) => this.formSubmit(e)}>
          <input type="text" name="email" placeholder="Email" value={this.state.email} onChange={this.handleEmailChange} required />
          <input type="password" name="password" placeholder="Password" value={this.state.password} onChange={this.handlePasswordChange} required />
          <button type="button">Login</button>
        </form>);
},
handleLogin: function() {
    //Login Function
}

A
Adhil mhdk

This will be an easiest method

const formValidator = (form) => {
    let returnData = {}
    console.log(form.length);

    for (let i = 0; i < form.length; i++) {
        const data = form[i]
        if (data.name != null && data.name != "") {
            returnData[data.name] = data.value;
        }
    }
    return returnData
}

In form simply

<form onSubmit={(e) => {
          e.preventDefault()
          let data = formValidator(e.currentTarget)
         }}>
          <RoundTextFiled name='app-id' style={{ marginTop: '10px', borderRadius: '20px' }} label="App id" fullWidth required />
          <RoundTextFiled name='api-hash' style={{ marginTop: '5px' }} label="Api hash" fullWidth required />
          <RoundTextFiled name='channel-id' style={{ marginTop: '5px' }} label="Channel id" fullWidth required />
          <Button type='submit' variant="contained" fullWidth style={{ padding: '10px', marginTop: '5px', borderRadius: '10px' }}>Login</Button>
        </form>

R
Radoslav Rác

I know this question is old but simplest solution that came to my mind is this:

<form onSubmit={(e) => handleLogin(e)}>
   <input type="text" name="email" placeholder="Email" />
   <input type="password" name="password" placeholder="Password" />
   <button type="submit">Login</button>
</form>

Your handle fuction:

const handleLogin = (e) => {
 e.preventDefault()
 const data = {
  email: e.target.elements.email.value,
  password: e.target.elements.password.value
 }
 console.log('FormData: ', data)
}

When you click on your login button you will see FormData in console in this format: FormData: {email: 'whatever you tiped here', password: 'also whatever you tiped here'}.

e.target.elements.email.value targets elements with specific name, in your case it is email and password.

After console.log in handleLogin you can do some verification logic and login logic.