controlled components in form - javascript

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

Related

Why react default form feature is not executing?

import React from "react";
export default class Form extends React.Component{
constructor(props) {
super(props);
this.inputRef = React.createRef();
this.state = {
value:""
}
}
// handleClick
handleClick = (e) => {
this.setState({ value: e.target.value })
console.log(e.target.value)
}
render() {
return <>
<h2>Typig ... {this.state.value} </h2>
<form>
<input type="text" ref={this.inputRef} onChange={this.handleClick} />
</form>
</>
}
}
I learnt that we can't change the value of any input tag in react, we have to do it manually by writing handler function but in the above code snippet i haven't change value explicitly then why here the default behaviour is not applied
In the above code snippet i have not change the value of the input tag explicitely then why react default feature is not applied here
Transform your Input like this:
<input type="text" value={this.state.value} ref={this.inputRef} onChange={e => this.handleClick} />

How do I make the text from an input field appear on the screen after it is submitted?

I would like to take whatever is entered into the input field and have it display on the screen after the user clicks submit. I know this seems super basic, but I am new to React. Thanks ahead of time!
import React, { Component } from 'react';
class TextInput extends Component {
constructor(props){
super(props);
this.state = { info: "", showName: false };
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(evt) {
evt.preventDefault();
this.setState({ info: evt.target.value })
}
handleSubmit(evt){
evt.preventDefault();
}
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
<input
type="text"
value={this.state.info}
onChange={this.handleChange}/>
<button>Submit</button>
</form>
//text from input appears here
</div>
);
}
}
export default TextInput;
If I understand correctly, you only want to display the text after the user clicks submit. In this case, you can use handleSubmit to set a special value in the state submittedInfo for example, and then display that value.
import React, { Component } from 'react';
class TextInput extends Component {
constructor(props){
super(props);
this.state = { info: "", submittedInfo: "", showName: false };
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(evt) {
evt.preventDefault();
this.setState({ info: evt.target.value })
}
handleSubmit(evt){
evt.preventDefault();
this.setState({submittedInfo: this.state.info})
}
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
<input
type="text"
value={this.state.info}
onChange={this.handleChange}/>
<button>Submit</button>
</form>
//text from input appears here
<div>{this.state.submittedInfo}</div>
</div>
);
}
}
export default TextInput;
Another approach could be to store in the state a boolean isSubmitted, signifying whether or not the user has submitted the form, and then displaying this.state.info if isSubmitted is true
You already has state like,
this.state = { info: "", showName: false };
You can use showName state to show your text,
return (
<div>
<form onSubmit={this.handleSubmit}>
<input
type="text"
value={this.state.info}
onChange={this.handleChange}/>
<button>Submit</button>
</form>
//text from input appears here
{this.state.showName && <div>{this.state.info}</div>}
</div>
);
And your handleSubmit function should be,
handleSubmit(evt){
evt.preventDefault();
this.setState({showName:true})
}

Passing handleSubmit() to child component does not modify parent's state

I am new to React and Javascript.
I am trying to have a user fill in a form that describes what a "Mob" should look like. When the user hits submit, I expect handleSubmit() (passed in through a parent) to modify the parent's state, which is an object. However, this behavior is not happening.
Here is the parent component, called App.
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
mob: new Mob("", "")
};
this.handleSubmit = this.handleSubmit.bind(this);
}
handleSubmit(event) {
event.preventDefault();
alert("A name was submitted: " + this.state.vnum + " event value: " + event.state.vnum);
const newMob = new Mob(event.state.vnum, event.state.shortDesc);
this.setState({
mob: newMob
});
}
render() {
return (
<div>
<MobForm mob={this.state.mob} onSubmit={() => this.handleSubmit} />
{console.log("parsed mob vnum: " + this.state.mob.vnum)}
</div>
);
}
}
The child component, called MobForm
class MobForm extends React.Component {
render() {
return (
<div>
<form onSubmit={this.props.onSubmit}>
<CreateStringInputField
name="vnum"
label="vnum:"
/>
<CreateStringInputField
name="shortDesc"
label="Short Desc:"
/>
<input type="submit" value="Submit" />
</form>
{console.log(this.state)}
</div>
);
}
}
Which is calling CreateStringInputField()
function CreateStringInputField(props) {
return (
<div name="row">
<label>
<b>{props.label}</b>
<br />
<input
type="text"
name={props.name}
label={props.label}
/>
</label>
</div>
);
}
And, in case it matters, here is what "Mob" looks like.
class Mob {
constructor(vnum, shortDesc) {
this.vnum = vnum;
this.shortDesc = shortDesc;
};
}
I expect to see {console.log("parsed mob vnum: " + this.state.mob.vnum)} print out the vnum as entered by a user. Instead, I see nothing. How can I achieve this expected output?
With React you won't need to work with plain classes. Instead, the class extends a provided React component (Component or PureComponent) or if you don't need state, then'll use plain functions that just return some JSX.
Working example: https://codesandbox.io/s/simple-form-kdh3w
index.js
import React from "react";
import { render } from "react-dom";
import MobForm from "./components/MobForm";
// simple function that returns "MobForm" and it gets rendered by ReactDOM
function App() {
return <MobForm />;
}
// applies "App" to a <div id="root"></div> in the public/index.html file
render(<App />, document.getElementById("root"));
components/MobForm/index.js (stateful parent component)
import React, { Component } from "react";
import Form from "../Form";
const initialState = {
vnum: "",
shortDesc: ""
};
// a stateful parent that manages child state
class MobForm extends Component {
constructor(props) {
super(props);
this.state = initialState;
// since the class fields are normal functions, they'll lose context
// of "this" when called as a callback. therefore, they'll need
// to be bound to "this" -- via bind, "this" is now referring to
// the Class, instead of the global window's "this")
this.handleChange = this.handleChange.bind(this);
this.handleReset = this.handleReset.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
// a reusable class field that stores an input's value via its "name"
// for example: [vnum]: "12345", [shortDesc]: "A number"
// using object destructuring for shorter syntax:
// [event.target.name]: event.target.value
handleChange({ target: { name, value } }) {
this.setState({ [name]: value });
}
// a class field to reset state
handleReset() {
this.setState(initialState);
}
// a class field to "submit" the form and alert what's currently in state
handleSubmit(event) {
// preventDefault prevents page refreshes
event.preventDefault();
// JSON.stringify allows you to print the contents of an object
// otherwise, you'll just see [object Object]
alert(JSON.stringify(this.state, null, 4));
// clears state after submitting form
this.handleReset();
}
render() {
return (
// passing down state via the spread operator, shorthand for
// "vnum={this.state.vum}" and "shortDesc={this.state.shortDesc}",
// as well as, passing down the class fields from above
<Form
{...this.state}
handleChange={this.handleChange}
handleReset={this.handleReset}
handleSubmit={this.handleSubmit}
/>
);
}
}
export default MobForm;
components/Form/index.js (a child function that returns some form JSX)
import React from "react";
import PropTypes from "prop-types";
import Input from "../Input";
// using object destructuring to pull out the MobForm's passed down
// state and fields. shorthand for using one parameter named "props"
// and using dot notation: "props.handleChange", "props.handleReset", etc
function Form({ handleChange, handleReset, handleSubmit, shortDesc, vnum }) {
return (
<form style={{ width: 200, margin: "0 auto" }} onSubmit={handleSubmit}>
<Input name="vnum" label="vnum:" value={vnum} onChange={handleChange} />
<Input
name="shortDesc"
label="Short Desc:"
value={shortDesc}
onChange={handleChange}
/>
<button type="button" onClick={handleReset}>
Reset
</button>{" "}
<button type="submit">Submit</button>
</form>
);
}
// utilizing "PropTypes" to ensure that passed down props match
// the definitions below
Form.propTypes = {
handleChange: PropTypes.func.isRequired,
handleReset: PropTypes.func.isRequired,
handleSubmit: PropTypes.func.isRequired,
shortDesc: PropTypes.string,
vnum: PropTypes.string
};
export default Form;
components/Input/index.js (a reuseable input function)
import React from "react";
import PropTypes from "prop-types";
// once again, using object destructuring to pull out the Form's
// passed down state and class fields.
function Input({ label, name, value, onChange }) {
return (
<div name="row">
<label>
<b>{label}</b>
<br />
<input
type="text"
name={name}
label={label}
value={value}
onChange={onChange}
/>
</label>
</div>
);
}
// utilizing "PropTypes" to ensure that passed down props match
// the definitions below
Input.propTypes = {
label: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
value: PropTypes.string,
onChange: PropTypes.func.isRequired
};
export default Input;
In this line
<MobForm mob={this.state.mob} onSubmit={() => this.handleSubmit} />
you are defining an anonymous function that returns your handleSubmit function.
In your form
<form onSubmit={this.props.onSubmit}>
onSubmit will execute the this.props.onSubmit which just returns the handleSubmit function but it wont execute it. To fix it just change MobForm to pass handleSubmit directly instead of passing it in an anonymous function:
<MobForm mob={this.state.mob} onSubmit={this.handleSubmit} />
To handle the submission correctly you need to convert your form inputs to managed components. See docs here
Something like this would be a good start:
class MobForm extends React.Component {
constructor(props) {
super(props);
this.state = {
vnum: '',
shortDesc: '',
};
this.handleChangeVnum = this.handleChangeVnum.bind(this);
this.handleChangeShortDesc = this.handleChangeShortDesc.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChangeVnum(event) {
this.setState({vnum: event.target.value});
}
handleChangeShortDesc(event) {
this.setState({shortDesc: event.target.value});
}
handleSubmit(event) {
this.props.onSubmit(this.state);
event.preventDefault();
}
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
<CreateStringInputField
name="vnum"
label="vnum:"
value={this.state.vnum}
onChange={this.handleChangeVnum}
/>
<CreateStringInputField
name="shortDesc"
label="Short Desc:"
value={this.state.shortDesc}
onChange={this.handleChangeShortDesc}
/>
<input type="submit" value="Submit" />
</form>
{console.log(this.state)}
</div>
);
}
}
And update CreateStringInputField()
function CreateStringInputField(props) {
return (
<div name="row">
<label>
<b>{props.label}</b>
<br />
<input
type="text"
name={props.name}
label={props.label}
value={props.value}
onChange={props.onChange}
/>
</label>
</div>
);
}
I was able to get my desired behavior by passing a function to MobForm which updates this.state.mob.
App
class App extends React.Component {
state = {
mob: new Mob("", "")
};
updateMob = newMob => {
this.setState({
mob: newMob
});
};
render() {
return (
<div>
<MobForm mob={this.state.mob} onSubmit={this.updateMob} />
</div>
);
}
}
I then made MobForm maintain vnum, shortDesc state that I could use in my onChange()
MobForm
state = { vnum: "", shortDesc: "" };
handleSubmit = event => {
event.preventDefault();
const mob = new Mob(this.state.vnum, this.state.shortDesc);
this.props.onSubmit(mob);
};
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
<CreateStringInputField
name="vnum"
value={this.state.vnum}
onChange={event => this.setState({ vnum: event.target.value })}
/>
<CreateStringInputField
name="short desc"
value={this.state.shortDesc}
onChange={event => this.setState({ shortDesc: event.target.value })}
/>
<input type="submit" value="Submit" />
</form>
</div>
);
}
}

Empty value when form is submitted for the first time

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

Triggering form's submit event manually in React doesn't check required inputs

im trying to find a way to trigger the submit event manually, just like a button with type="submit" would.
I found a way to do that, from another post here, but unfortunately it doesn't check if the required inputs contain values and submits the form even when no text was typed into the input:
https://codesandbox.io/s/q92855nz3w
or
import React from "react";
import { render } from "react-dom";
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}>
<div onClick={this.handleSubmit}>SOME DIV</div>
<label>
Name:
<input
type="text"
value={this.state.value}
onChange={this.handleChange}
required
/>
</label>
<input type="submit" value="Submit" />
</form>
);
}
}
render(<NameForm />, document.getElementById("root"));
Thanks for any help in advance!
Found a solution to my question:
var form = document.getElementById("form");
form.reportValidity();

Categories

Resources