Empty value when form is submitted for the first time - javascript

I have the following code which is intended to get the input fed to ToggleForm component (which is a form) and store it in employeeData state. However, the problem is that whenever I press the submit button of ToggleForm for the first time after execution, "" value gets stored first in the employeeData state and it is only after I click the submit button for the second time that the data fed in the form comes to employeeData.
This must be a minor mistake. But I am not being able to figure it out.
import React from "react";
import ToggleForm from "./ToggleForm";
let employee = "";
class Home extends React.Component {
constructor(){
super();
this.state = {
employeeData: ""
};
}
addEmployee(e) {
e.preventDefault();
let name = e.target.name.value;
let address = e.target.address.value;
let salary = e.target.salary.value;
this.setState({
employeeData: [...this.state.employeeData, { name, address, salary }]
});
employee = [...this.state.employeeData];
console.log(employee);
}
render() {
return (
<div className="container">
<ToggleForm addEmployee={this.addEmployee.bind(this)}/>
</div>
);
}
}
export default Home;
Here is the ToggleForm component:
import React from 'react';
class ToggleForm extends React.Component {
render(){
return(<div>
<br/>
<h3>Add a new employee</h3>
<hr/>
<form className="form-group" onSubmit = {this.props.addEmployee}>
<input className="form-control" type="text" name="name" placeholder="Name of the employee"/><br/>
<input className="form-control" type="text" name="address" placeholder="Address of the employee"/><br/>
<input className="form-control" type="text" name="salary" placeholder="Salary of the employee"/><br/>
<input type="submit" className="btn btn-primary"/>
</form>
</div>)
}
}
export default ToggleForm;

setState is async and fortunately accepts an optional callback. Using the callback, you can access the most current value of state.
this.setState({
employeeData: [...this.state.employeeData, { name, address, salary }]
}, () => {
employee = [...this.state.employeeData];
});

Because setState is async so your need to setState in the component Toggle form when the text is change before ship it the parent component.
For example:
<input
onChange={this.handleChange}
className="form-control"
type="text"
name="name"
value={this.state.name}
placeholder="Name of the employee"
/>
<br />
Function handleChange:
handleChange = (e) => {
this.setState({ [e.target.name]: e.target.value });
console.log(e.target.value)
};
And then ship it to the parent:
handleSubmit = e => {
e.preventDefault();
const { name, address, salary } = this.state;
this.props.addEmployee({ name, address, salary });
};
Check my code here: https://codesandbox.io/s/ww5331jrxl

There are few basic correction in your components:
User super(); in the constructor before this.setState();
If you are not using this.state.employeeData, then don't set it in the state.
If you set the state then you will get the employeeData in the callback function as described by #Andy or you can use the following:
employee = [...this.state.employeeData, { name, address, salary }]

Related

Any way I can declare the event object rather than in the params of a function?

is there anyways I can put the e object inside of the handleSubmit function? I already have a parameter there, and if I add the e to the params then it will give me an error depending of order of params and say "TypeError: e.preventDefault is not a function" if handleSubmit=(e, priorityLevelData)=> {. .... or "TypeError: Cannot read property 'preventDefault' of undefined" if handleSubmit=(priorityLevelData, e)=> {
import React from "react";
import PrioritySelector from "./PrioritySelector";
import { connect } from "react-redux";
class TodoForm extends React.Component {
/*submit handler to grab input from the input references and store them
in the "todoData" object. Then dispatches an action type and payload
capturing the data. Then clears the input*/
handleSubmit=(e, priorityLevelData)=> {
e.preventDefault()
const todoTitle = this.getTodoTitle.value;
const description = this.getDescription.value;
const priorityLevel = priorityLevelData;
const todoData = {
id: Math.floor(Math.random()*1000),
todoTitle,
description,
priorityLevel,
editing: false
}
this.props.dispatch({type:"ADD_TODO", todoData })
this.getTodoTitle.value = "";
this.getDescription.value = "";
}
render() {
console.log(this.props, "TODOFORMPROPS")
return(
<div>
<form onSubmit={this.handleSubmit}>
<input type="text" ref={(input)=> this.getTodoTitle=input} placeholder="Enter Todo" required/>
<input type="text" ref={(input)=> this.getDescription=input} placeholder="Enter Description" required/>
<PrioritySelector getData={this.handleSubmit} />
<button>Add Todo</button>
</form>
</div>
)
}
}
const mapStateToProps = (state) => {
return {
priorityLevel: state
}
}
export default connect(mapStateToProps)(TodoForm);
do this:
<form onSubmit={e => this.handleSubmit(e)}>
You can also pass any other additional data to handleSubmit(), for example, this.state:
<form onSubmit={e => this.handleSubmit(e, this.state)}>

React - How to get state data from a child component?

I am just starting to learn react and I'm currently building a form, so far I've created a parent component 'Form' and I've separated the rest of the inputs as components and each component has its own state. My question is how to get that state data from the children's components and use it in the parent component 'Form' when submitting the form?
Here is my parent component
import React, { Component } from "react";
import Name from "components/Name";
import Email from "components/Email";
import Select from "components/Select";
import Bio from "components/Bio";
class Form extends Component {
handleSubmit = event => {
event.preventDefault();
};
render() {
return (
<form onSubmit={this.handleSubmit}>
<div className="shape rectangle"></div>
<div className="shape triangle"></div>
<div className="shape circle"></div>
<Name />
<Email />
<Select />
<Bio />
<button type="submit" className="btn">
Submit
</button>
</form>
);
}
}
export default Form;
And one of the child component
import React, { Component } from "react";
// Create Name component for name && email inputs
class Name extends Component {
constructor(props) {
super(props);
this.state = {
firstName: "",
lastName: ""
};
}
// Handle first name input on change event
handleFirstNameChange = event => {
this.setState({
firstName: event.target.value
});
};
// Handle last name input on change event
handleLastNameChange = event => {
this.setState({
lastName: event.target.value
});
};
// Render labels and name inputs
render() {
const { firstName, lastName } = this.state;
return (
<div className="form-names">
<label htmlFor="firstName">Name</label>
<br/>
<input
type="text"
name="firstName"
value={firstName}
placeholder="First Name"
id="firstName"
onChange={this.handleFirstNameChange}
/>
<input
type="text"
name="lastName"
value={lastName}
placeholder="Last Name"
id="lastName"
onChange={this.handleLastNameChange}
/>
</div>
);
}
}
export default Name;
To accomplish this you would need to "Lift the state" up to a common parent component (known as ancestor component), in your case, this would be the <Form> component. Then you would pass down the values to each corresponding child component as props.
It would look something like this:
import React, { Component } from "react";
import Name from "./Name";
// More imports go here..
class Form extends Component {
state = {
firstName: "",
lastName: ""
};
handleSubmit = event => {
event.preventDefault();
};
// Handle first name input on change event
handleFirstNameChange = event => {
this.setState({
firstName: event.target.value
});
};
// Handle last name input on change event
handleLastNameChange = event => {
this.setState({
lastName: event.target.value
});
};
render() {
return (
<form onSubmit={this.handleSubmit}>
<Name
firstName={this.state.firstname}
lastName={this.state.lastName}
handleFirstNameChange={this.handleFirstNameChange}
handleLastNameChange={this.handleLastNameChange}
/>
{/* More components go here.. */}
<p>Current state:</p>
{JSON.stringify(this.state)}
</form>
);
}
}
export default Form;
Working example
More info: Lifting state up from the official React docs.
you can use a single state in the parent component , and pass the firstname and lastname in the props, your code will became:
import React, { Component } from "react";
import Name from "components/Name";
import Email from "components/Email";
import Select from "components/Select";
import Bio from "components/Bio";
class Form extends Component {
state = {
firstName: '',
lastName: '',
}
onChange = (e) => {
this.setState({[e.target.name] : e.target.value})
}
handleSubmit = event => {
event.preventDefault()
}
render() {
return (
...
<Name onChange={this.onChange} firstName={this.state.firstName} lastName={this.state.lastName} />
...
<button type="submit" className="btn">
Submit
</button>
</form>
)
}
}
export default Form;
and your child component became:
import React, { Component } from "react";
// Create Name component for name && email inputs
class Name extends Component {
render() {
const { firstName, lastName , onChange} = this.props;
return (
<div className="form-names">
<label htmlFor="firstName">Name</label>
<br/>
<input
type="text"
name="firstName"
value={firstName}
placeholder="First Name"
id="firstName"
onChange={onChange}
/>
<input
type="text"
name="lastName"
value={lastName}
placeholder="Last Name"
id="lastName"
onChange={onChange}
/>
</div>
);
}
}
export default Name;
My thought is to do sth like the following, passing the data as props to the component. But it doesn't make sense to me as when you click the submit button, the form gather data from the parent component. It may better keep the form in the parent component and group all the input data in state.
import React, { Component } from "react";
import Name from "components/Name";
import Email from "components/Email";
import Select from "components/Select";
import Bio from "components/Bio";
class Form extends Component {
constructor(props){
super(props)
this.state = {
firstName = "",
lastName = "",
(and other input data you need)...
}
}
handleSubmit = event => {
event.preventDefault();
};
render() {
return (
<form onSubmit={this.handleSubmit}>
<div className="shape rectangle"></div>
<div className="shape triangle"></div>
<div className="shape circle"></div>
<Name firstName={this.state.firstName} lastName={this.state.lastName}/>
<Email (inputData as props)/>
<Select (inputData as props)/>
<Bio (inputData as props)/>
<button type="submit" className="btn">
Submit
</button>
</form>
);
}
}
export default Form;
You could either use something like Context or Redux, which would allow you to create a universal store and retrieve state from any component or create methods in your parent component, pass them down to it's children, and invoke them from within that child component. For example, you could have handleFirstNameChange in Form and pass it down to Name. In doing so you'd now keep props like name in Form as well and pass that down as well.

How to use setState with fetch API on form submit in React?

I'm new to React and wanted to play around with the fetchAPI. I want to use the GoogleBooks API to display a list of books that matches a query string which the user can enter in an input field.
I've managed to make the call to the Google API and have used setState but I can't get the state to stick. If I want to render the state after fetching the data, the state appears as undefined.
I have a feeling that React renders the HTML first and then sets the state after making the API call.
I'd appreciate if you can have a look at my component.
Thank you!
import React, {Component} from 'react'
class Search extends Component{
constructor(props){
super(props)
this.state = {
books: []
}
this.books = this.state.book
this.title = ''
this.handleChange = (e) => {
this.title = e.target.value
}
this.handleSubmit = (e) => {
let results = '';
e.preventDefault();
const apiKey = 'AIzaSyBuR7JI6Quo9aOc4_ij9gEqoqHtPl-t82g'
fetch(`https://www.googleapis.com/books/v1/volumes?q=${this.title}&key=${apiKey}`)
.then(response => response.json()).then(jsonData => {
this.setState({
books: jsonData
})
console.log(jsonData)
})
}
}
render(){
return(
<div className="col-md-12">
<form onSubmit={this.handleSubmit} className="form-group">
<label>Enter title</label>
<input onChange={this.handleChange} className="form-control" ref="title" type="text" placeholder="Enter title"></input>
<button type="submit" className="btn btn-primary">Submit</button>
</form>
<div>
<li>this.state.books</li>
</div>
</div>
You have:
<li>this.state.books</li>
This is just rendering a string.
Instead, you can use map to show data from book you get from the response.
Here's a working example:
<div>
{
this.state.books.items &&
this.state.books.items.map(book => <li>{book.etag}</li>)
}
</div>

ReactJS: doing calculations to setState, and updating

I'd like to have two input numeric fields, value1 and value2 and add the values of those two fields. The sum will then be saved in this.state.sum. I also don't want to use a "submit" button. Some code:
import React, { Component } from 'react';
import addNumbers from "./components/addNumbers"
class App extends Component {
constructor() {
super()
this.state = {
value1: 10,
value2: 3,
sum: 13
}
this.handleChange = this.handleChange.bind(this)
}
handleChange(event) {
//first setState call works fine
this.setState({[event.target.name]: event.target.value})
//call addNumbers function
const sum = addNumbers(this.state.value1, this.statevalue2)
//this second setState() call will lag by one step
this.setState({sum: sum})
console.log(newBarData)
}
render() {
return (
<div>
<form>
<input name="value1" type="number" placeholder="10" onBlur={this.handleChange} />
<input name="value2" type="number" placeholder="3" onBlur={this.handleChange} />
</form>
</div>
);
}
}
export default App;
What happens is: the second setState() call won't be effected until I make a second change on the page. This is quite a common issue, but I'm not sure how to work around it. I'd like for it to be able to proc immediately after the user updates the form. In the actual use-case, there'll be some calculations based on user-input values, then visualized on a graph (plotly.js), and I want to be able to update the graph "live" after each input field change.
You can use currying to create different handler in one piece of code (see: makeHandleChange). Then you can use componentDidUpdate to check if the value of your inputs changed to update your sum state value.
import React, { Component } from 'react';
import addNumbers from "./components/addNumbers"
class App extends Component {
state = {
value1: 10,
value2: 3,
sum: 13
}
componentDidUpdate(lastProps, lastState) {
const valueChanged = this.state.value1 !== lastState.value1 || this.state.value2 != lastState.value2
if (valueChanged) {
const sum = addNumbers(this.state.value1, this.state.value2)
this.setState({ sum })
}
}
makeHandleChange = id => ({ target: { value } }) => {
this.setState({
[`value${id}`]: value
})
}
render() {
return (
<div>
<form>
<input name="value1" type="number" placeholder="10" onBlur={this.makeHandleChange(1)} />
<input name="value2" type="number" placeholder="3" onBlur={this.makeHandleChange(2)} />
<div>the sum is: {this.state.sum}</div>
</form>
</div>
);
}
}
export default App;

controlled components in form

I have this form where I want the name and gender of the user and I change state accordingly but my code seems to fail as the output don't match what i want.
Here is the code.
class Addoption extends React.Component{
constructor(props){
super(props);
this.state={
name:'',
gender:''
};
this.handleaddoption=this.handleaddoption.bind(this);
}
handleaddoption(event){
event.preventDefault();
const nameofuser=event.target.elements.nametext.value;
const genderofuser=event.target.elements.gendertext.value;
this.setState({
name:nameofuser,
gender:genderofuser
});
console.log(this.state.name);
console.log(this.state.gender);
}
render(){
return(
<div>
<form >
<input type="text" name="nametext" placeholder="name" onChange={this.handleaddoption}/>
<input type="text" name="gendertext" placeholder="gender" onChange={this.handleaddoption}/>
</form>
</div>
);
}
}
export default App;
When the user enters the name and the age ,I don't see his age and name in console instead error which says cannot read Cannot read property 'nametext' of undefined.
I tried one more method even that doesn't seem to work.
handleaddoption(event){
event.preventDefault();
const nameofuser=event.target.elements.nametext.value;
const genderofuser=event.target.elements.gendertext.value;
this.setState({
name:nameofuser,
gender:genderofuser
});
console.log(this.state.name);
console.log(this.state.gender);
}
render(){
return(
<div>
<form onSubmit={this.handleaddoption}>
<input type="text" name="nametext" placeholder="name" />
<input type="text" name="gendertext" placeholder="gender"/>
</form>
</div>
);
}
}
You need to handle the event a little different.
Your handler function receives an event object, and the event.target is actually the input.
Change your input names so you can directly reference the state with them.
Then check out the component lifecycle, you can't see the updates because of how the component rendering works in react. Notice I used the componentDidUpdate method which runs after the setState is completed and there the logs show the new state.
Here's the link to the lifecycle docs: https://reactjs.org/docs/state-and-lifecycle.html
import React from 'react'
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = { name: '', gender: '' }
this.handleChange = this.handleChange.bind(this)
}
componentDidUpdate() {
console.log('gender 1', this.state.gender)
console.log('name 1', this.state.name)
}
handleChange(e) {
this.setState({
[e.target.name]: e.target.value
});
console.log('gender', this.state.gender)
console.log('name', this.state.name)
}
render() {
return (
<form action="">
<input type="text" name="gender" value={this.state.gender || ''} onChange={this.handleChange}/>
<input type="text" name="name" value={this.state.name || ''} onChange={this.handleChange}/>
</form>
)
}
}
export default MyComponent

Categories

Resources