Form data won't bind to React component using onChange() - javascript

I've been coding up an email service for myself, and got stuck on this React form not changing state when form data is entered. Tracked down the main problem to this React form not changing state via onChange()...
How could I bind state to the React form that updates on every keystroke? Using this form data to pass later into an API call.
'''
class AddContact extends Component {
constructor(props) {
super(props);
this.state = {
'name': '',
'company': '',
'linkedin': '',
'department': '',
'email': 'Not available'
}
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({ value: event.target.value });
}
handleSubmit(event) {
}
render() {
return(
<div>
<form onSubmit = {this.handleSubmit}>
<label>
Name
<input type="text" value={this.state.name} onchange = {this.handleChange}/>
</label>
<label>
Company
<input type="text" value={this.state.company} onchange = {this.handleChange}/>
</label>
<label>
LinkedIn Profile
<input type="text" value={this.state.linkedin} onchange = {this.handleChange}/>
</label>
<label>
Department
<input type="text" value={this.state.department} onchange = {this.handleChange}/>
</label>
<label>
Email (Optional)
<input type="text" value={this.state.email} onchange = {this.handleChange}/>
</label>
<input type="submit" value="Submit" />
</form>
</div>
);
}
}
'''

You're not changing the actual form values in state. This line: this.setState({ value: event.target.value }); Changes the value of the value key in the state. So if you were to call this this.setState({ value: 'potato' }); then your state would look like this:
this.state = {
'name': '',
'company': '',
'linkedin': '',
'department': '',
'email': 'Not available',
'value': 'potato'
}
So you need to pass in the actual field name you're trying to change into the onChange function. And that's another thing, you have a typo. The handler is called onChange, with camel case. Yours is incorrectly all lower case.
So here's a correct example of updating the 'name' field. Firstly, update your handleChange function to this:
handleChange(field, value) {
this.setState({ [field]: value });
}
Then change your inputs to be like this:
<input type="text" value={this.state.name} onChange={e => this.handleChange('name', e.target.value)}/>

Related

How to clear controlled forms in react-redux

I'm trying to clear a form in my react-redux component. I know that I should be able to clear them using setState() after a submit, but since I'm receiving all my data as props (via the redux store) is there an easy way to do this within the component itself?
class Postform extends Component {
constructor(props){
super(props)
this.state={
propertyName: ' enter name ',
footage: ' size in sqft ',
address: ' full address ',
price: ' $ 00.00 '
}
}
onChange =(e)=>{
this.setState({ [e.target.name] :e.target.value});
}
onSubmit = (e) =>{
e.preventDefault()
const newListing = {
propertyName: this.state.propertyName,
footage: this.state.footage,
address: this.state.address,
price: this.state.price
}
this.props.newProperty(newListing)
// my attempt to reset the state of the form (unsure how to accomplish this?)
this.setState({
propertyName: '',
footage: '',
address: '',
price: ''
})
};
render() {
return (
<div className="form">
<h2>Add Listing</h2>
<form onSubmit = {this.onSubmit}>
<div>
<label>your listing name</label><br/>
<input name="propertyName" type="text" onChange={this.onChange} placeholder={this.state.propertyName} />
</div>
<div>
<label>listing size </label><br/>
<input name="footage" onChange={this.onChange} placeholder={this.state.footage} />
</div>
<div>
<label>listing location </label><br/>
<input name="address" onChange={this.onChange} placeholder={this.state.address} />
</div>
<div>
<label>desired price </label><br/>
<input name="price" onChange={this.onChange} placeholder={this.state.price} />
</div>
<br/>
<button className="submitbtn" type="submit">Submit</button>
</form>
</div>
)
}
}
Postform.propTypes = {
newProperty: PropTypes.func.isRequired,
new: PropTypes.object
}
const mapStateToProps = state =>({
listings: state.listings.properties,
new: state.listings.newListing
});
export default connect(mapStateToProps, {newProperty})(Postform)
I checked around online and found a few solutions. I wanted to see if anyone could tell me based on my code if there would be a preferred way of achieving this?
Here all the different methods I found not sure which I should use based on my component though : https://redux-form.com/6.0.0-alpha.7/docs/faq/howtoclear.md/
Your approach seems valid to me, I would only do the job in a bit 'lazy' manner:
this.setState(Object.keys(this.state).forEach(key => this.state[key] = ''))
import { initialize, reset } from 'redux-form';
dispatch(initialize('formName', {})); // Clear form
// or
dispatch(reset('formName'));

onChange function for input fields not working

Trying to change the state of input fields but onChange doesn't seem to be working and input fields are disabled (not letting me type)
constructor() {
super();
this.state = {
title: '',
length: '',
image: '',
source: '',
available: '',
date: '',
errors: {}
}
this.onChange = this.onChange.bind(this);
}
onChange(e) {
this.setState({ [e.target.name]: e.target.value });
}
<input value={this.state.length} onChange={this.onChange} type="text"
className="form-control" name="email" placeholder="e.g. 3:36" />
2 things :
Your setState function updates a value in the state based on the event's target node name, and here, the name ("email") does not match the value you are getting in your state (length).
The second error is that your onChange is not bound to your class, calling this.setState will return an error.
You can solve it by changing the value variable in your input :
<input value={this.state.email} onChange={this.onChange} type="text" className="form-control" name="email" placeholder="e.g. 3:36"/>
And converting onChange to an arrow function (or binding it) :
onChange = e => {
this.setState({ [e.target.name]: e.target.value });
}
** UPDATED:
** your main fault, is your are linking your input to a state variable called "length", and the name of the input is "email", they should be the same.
You can make it easier for yourself, you don't need to bind at constructor at all, IF you use arrow functions, so, if your write your function like this:
onChange = (e) => {
this.setState({ [e.target.name]: e.target.value });
}
your constructor and state, will be like this:
constructor() {
super();
this.state = {
title: '',
email: '',
image: '',
source: '',
available: '',
date:'',
errors: {}
}
}
And, in JSX, you still pass your properties the same way, like this:
<input value={this.state.email} onChange={this.onChange} type="text"
className="form-control" name="email" placeholder="e.g. 3:36" />
Full working demo:

Update data in form. Stay editable. (React with Redux, sans redux-form)

Problem
I have a list of people. I want to:
Select a user to edit by clicking on their name.
Edit that user's information, so I can click the submit button and update the list.
If I click on a different name, I want to switch to that person's information without having to deliberately close the form first.
Everything works until #3. When I click on another person, the form, itself, does NOT update.
My Code
Update Component for the update form:
const UpdateForm = ({ updatePerson, personToUpdate, handleInputChange }) => {
let _name, _city, _age, _id;
const submit = (e) => {
e.preventDefault();
updatePerson({
name: _name.value,
city: _city.value,
age: _age.value,
_id: _id.value
});
};
return (
<div>
<form onSubmit={submit}>
<h3>Update Person</h3>
<label htmlFor="_id">Some Unique ID: </label>
<input type="text" name="_id" ref={input => _id = input} id="_id" defaultValue={personToUpdate._id} onChange={input => handleInputChange(personToUpdate)} required />
<br />
<label htmlFor="name">Name: </label>
<input type="text" name="name" ref={input => _name = input} id="name" defaultValue={personToUpdate.name} onChange={input => handleInputChange(personToUpdate)} />
<br />
<label htmlFor="city">City: </label>
<input type="text" name="city" ref={input => _city = input} id="city" defaultValue={personToUpdate.city} onChange={input => handleInputChange(personToUpdate)} />
<br />
<label htmlFor="age">Age: </label>
<input type="text" name="age" ref={input => _age = input} id="age" defaultValue={personToUpdate.age} onChange={input => handleInputChange(personToUpdate)} />
<br />
<input type="submit" value="Submit" />
</form>
</div>
);
};
export default UpdateForm;
Relevant parts of Person Component:
class Person extends Component {
nameClick() {
if (this.props.person._id !== this.props.personToUpdate._id) {
this.props.setForUpdate(this.props.person);
this.forceUpdate();
}
else {
this.props.toggleUpdatePersonPanel();
}
}
render() {
return (
<div>
<span onClick={this.nameClick}>
{this.props.person.name} ({this.props.person.age})
</span>
</div>
);
}
}
export default Person;
Relevant parts of PeopleList, which holds Persons:
class PeopleList extends Component {
render() {
return(
<div>
{this.props.people.map((person) => {
return <Person
key={person._id}
person={person}
updatePersonPanel={this.props.updatePersonPanel}
setForUpdate={this.props.setForUpdate}
personToUpdate={this.props.personToUpdate}
/>;
})}
</div>
);
}
} // end class
export default PeopleList;
Form Reducer, with just the relevant actions:
export default function formReducer(state = initialState.form, action) {
let filteredPeople;
switch (action.type) {
case TOGGLE_UPDATE_PANEL:
return Object.assign({}, state, { updatePersonPanel: false }, { personToUpdate: {
_id: "",
name: "",
city: "",
age: ""
}});
case SET_FOR_UPDATE:
return Object.assign({}, state, { personToUpdate: action.person }, { updatePersonPanel: true });
case UPDATE_RECORD:
filteredPeople = state.people.filter((person) => {
return person._id === action.person._id ? false : true;
}); // end filter
return Object.assign({}, state, { people: [ ...filteredPeople, action.person] }, { personToUpdate: {
_id: "",
name: "",
city: "",
age: ""
}}, { updatePersonPanel: false });
case HANDLE_INPUT_CHANGE:
return Object.assign({}, state, { personToUpdate: action.person });
default:
return state;
}
}
The relevant parts of my Initial State file:
form: {
people: [
{
_id: "adfpnu64",
name: "Johnny",
city: "Bobville",
age: 22
},
{
_id: "adf2pnu6",
name: "Renee",
city: "Juro",
age: 21
},
{
_id: "ad3fpnu",
name: "Lipstasch",
city: "Bobville",
age: 45
}
],
updatePersonPanel: false,
personToUpdate: {
_id: "",
name: "",
city: "",
age: ""
},
}
Attempts at a Solution( so far)
I have attempted to make the component a completely controlled component, by switching the form attribute to value instead of defaultValue. When I do this, the names switch just fine, but the form becomes unchangeable and useless.
My Questions
Almost all of the solutions to these kind of issues either recommend using redux-form or supply two-way binding solutions that work fine in React without reduce. I want to know how to do this with Redux without using redux-form or anything extra if possible. Is there a way to resolve this without touching lifecycle methods?
Conclusion (For now)
Well, for now, I settled for making my form uncontrolled and used some classic Js DOM methods and a lifecycle method to control the form. For whatever reason, once I employed some of the answer suggestions my browser ate up my CPU and crashed, presumably because there was some kind of infinite loop. If anyone has some further recommendations, I'd really appreciate it. For now I settle for this:
class UpdateForm extends Component {
constructor(props){
super(props);
this.submit = this.submit.bind(this);
}
componentWillReceiveProps(nextProps) {
if (nextProps.personToUpdate._id !== this.props.personToUpdate._id) {
document.getElementById("name").value = nextProps.personToUpdate.name;
document.getElementById("age").value = nextProps.personToUpdate.age;
document.getElementById("city").value = nextProps.personToUpdate.city;
}
}
submit(e) {
e.preventDefault();
this.props.updatePerson({
name: document.getElementById("name").value,
city: document.getElementById("city").value,
age: document.getElementById("age").value,
_id: this.props.personToUpdate._id
});
}
render() {
return (
<div>
<form onSubmit={this.submit}>
<h3>Update Person</h3>
Unique ID: {this.props.personToUpdate._id}
<br />
<label htmlFor="name">Name: </label>
<input type="text" name="name" id="name" ref="name" defaultValue={this.props.personToUpdate.name} required />
<br />
<label htmlFor="city">City: </label>
<input type="text" name="city" id="city" ref="city" defaultValue={this.props.personToUpdate.city} required />
<br />
<label htmlFor="age">Age: </label>
<input type="text" name="age" id="age" ref="age" defaultValue={this.props.personToUpdate.age} required />
<br />
<input type="submit" value="Update" />
</form>
</div>
);
}
} // end class
export default UpdateForm;
I'll be soon exploring redux-form because it is evident that forms as inputs and outputs are a wonky business. For now, my little app works.
Yes there is and you are on the right path. The way is to use value instead of defaultValue but you have to read the value from a state and then use the onChange handler to modify the state.
Something like
this.state = {inputText:''}
Then in the input field
<input value={this.state.inputText} onChange={this.handleChange}/>
And the handleChange function will be
handleChange(event){
this.setState({inputText:event.target.value})
}
Remember to bind the handleChange event in the constructor so you can pass it as this.handleChange in the input field's onChange prop.
Something like this
this.handleChange = this.handleChange.bind(this)
https://facebook.github.io/react/docs/forms.html - Here are the official docs regarding it
Also if you want to do it in redux the same sort of logic applies where this will be the input field
<input value={this.props.inputText} onChange={this.props.handleChange}/>
where inputText and handleChange are redux state and action respectively passed to the component via props
For your case I guess it has to be something like where you are 'reading' values from the people array and the action bound to the onChange modifies that value in the people array in the state.
<--EDIT-->
How it can be done for the case in point. Pass the people in the redux state as a people prop to the component. Pass an action changePeople(newPeople) to the component as a prop which takes an argument newPeople and changes the people in the redux state to have the value newPeople. Since people is nested in form you'll have to do some Object.assign etc to modify the state.
Now in the component using the people props populate the checkboxes using a map function. The map function takes a second parameter index so for each checkbox have a function which sets the local state variable currentPerson to the value of the index
this.props.people.map((person,index) =>
return <Checkbox onClick={()=>this.setState(currentPerson:index)}/>
)
So everytime you click on a checkbox the currentPerson points to the corresponding index.
Now the input fields can be
<input value={this.props.people[this.state.currentPerson].name} onChange={this.handleChange.bind(this,'name')}/>
This is for the 'name' input field. It reads from the currentPerson index of the people array which has been passed down as a prop.
This is how the handleChange will be like
handleChange(property,event){
const newPeople = [
...this.props.people.slice(0, this.state.currentPerson),
Object.assign({}, this.props.people[this.state.currentPerson], {
[property]: event.target.value
}),
...this.props.people.slice(this.state.currentPerson + 1)
]
this.props.changePeople(newPeople)
}
The handleChange takes a property (so you don't have to write separate handlers for each input field). The newPeople basically modifies the element at current index this.state.currentPerson in the people passed from props (ES6 syntax being used here. If you have doubts do ask). Then it is dispatched using the changePeople action which was also passed as props.

this.state.value is undefined despite being set

I am new to reactjs. I have taken sample code from the react documentation (https://facebook.github.io/react/docs/forms.html) and modified it very slightly to create the following class:
class LoginView extends React.Component {
constructor(props) {
super(props);
this.state = {email: '',
password: ''};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
alert(this.state.email);
this.setState({email: event.target.email});
this.setState({password: event.target.password});
}
handleSubmit(event) {
alert(this.state.email);
var response = fetch('http://0.0.0.0:5000/v1/authtoken/', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*'
},
body: JSON.stringify({
email: this.state.email,
password: this.state.password,
})
}).then((response) => response.json())
var token = response.token
alert([token])
event.preventDefault();
}
render() {
return (
<div className="login-form">
<form onSubmit={this.handleSubmit}>
<div className="form-group">
<label htmlFor="email">Email address:</label>
<input type="email" className="form-control" id="email" value={this.state.email} onChange={this.handleChange}/>
</div>
<div className="form-group">
<label htmlFor="pwd">Password:</label>
<input type="password" className="form-control" id="pwd" value={this.state.password} onChange={this.handleChange}/>
</div>
<div className="checkbox">
<label><input type="checkbox"/> Remember me</label>
</div>
<button type="submit" className="btn btn-default">Submit</button>
</form>
</div>
);
}
}
export default LoginView
The purpose of the class is to present a login form which will post the value of the email and password fields to an endpoint. I am pulling my hair out over the way the this.state object is behaving.
Note that there is an alert in both handleSubmit and handlechange. These alerts should contain the value of the email field, but they instead contain the value undefined. What is confusing me is that the fields themselves, which also present values from this.state.email, actually work. This means that state is somehow being saved, but it just isn't available within the functions.
I've been pouring over this for hours and am at a loss as to how I could be getting this behavior. Any help would be very appreciated.
In handleChange(), event.target.email and event.target.password is not valid.
event is the onChange event for the particular field that is changing. event.target returns the DOMEventTarget (https://facebook.github.io/react/docs/events.html) which is a reference to the object that dispatched the event.
So in your case, event.target is the input field that triggered the onChange.
You will need to update this to:
// If you use ES6 you can do this to use `event.target.type` as a key
handleChange(event) {
this.setState({
[event.target.type]: event.target.value
});
}
// Otherwise, you can do it like this
handleChange(event) {
var newState = {};
newState[event.target.type] = event.target.value;
this.setState(newState);
}
event.target.type will return the <input> type which for your case is either 'email' or 'password'. and event.target.value will return the value which you want to update.
Additional note: Calling .setState(nextState) will perform a shallow merge of nextState into current state. So you do not need to update both email and password in your handleChange function. https://facebook.github.io/react/docs/react-component.html#setstate

Uncaught TypeError: Cannot read property 'value' of undefined in REACT JS

I am creating a login form using REACT JS as front-end and PHP as back-end. I am trying to get input values. Code is as below:
import React, { Component} from 'react';
import ReactDOM from 'react-dom';
import {Button, IconButton} from 'react-toolbox/lib/button';
import Input from 'react-toolbox/lib/input';
export default class Login extends React.Component {
constructor() {
super();
this.state = {email: ''};
this.state = {password: ''};
this.onSubmit = this.onSubmit.bind(this);
this.handleEmailChange = this.handleEmailChange.bind(this);
this.handlePasswordChange = this.handlePasswordChange.bind(this);
}
handleEmailChange(e) {
this.setState({email: e.target.value});
}
handlePasswordChange(e) {
this.setState({password: e.target.value});
}
onSubmit() {
fetch('http://xyz/check-login', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
email: this.state.email,
password: this.state.password,
})
})
}
And form is as below:
<form name="Login">
<Input type="text" name="email" value={this.state.email} placeholder="Email Id" className="form-control" onChange={this.handleEmailChange} />
<Input name="password" value={this.state.password} placeholder="Password" type="password" className="form-control m-b-10" onChange={this.handlePasswordChange} />
<Button type="button" className="m-t-20 orange" label="Sign in " onClick={this.onSubmit} />
</form>
But getting the following error:
Uncaught TypeError: Cannot read property 'value' of undefined
I am using react-toolbox. So, I am using component from https://github.com/react-toolbox/react-toolbox/blob/dev/components/input/Input.js and component from https://github.com/react-toolbox/react-toolbox/blob/dev/components/button/Button.js.
First of all, what are <Input ..../> and <Button .../>? Are they your components or they are only form input fields?
I suppose that they are only form fields, thus they need to be in lower case <input ..../> , <button .../>.
Try to bind your functions inside render, like : this.functionName.bind(this).
This is a working code :
class Test extends React.Component {
constructor(props){
super(props);
this.state = {
email: '',
password: '',
};
}
handleEmailChange(e) {
this.setState({email: e.target.value});
}
handlePasswordChange(e) {
this.setState({password: e.target.value});
}
render(){
return (
<div>
<form name="Login">
<input type="text" name="email" value={this.state.email} placeholder="Email Id" className="form-control" onChange={this.handleEmailChange.bind(this)} />
<input name="password" value={this.state.password} placeholder="Password" type="password" className="form-control m-b-10" onChange={this.handlePasswordChange.bind(this)} />
<button type="button" className="m-t-20 orange" label="Sign in " onClick={this.onSubmit}>Sign in</button>
</form>
</div>
)
}
}
React.render(<Test />, document.getElementById('container'));
Here is the fiddle.
UPDATE
I tested it here :
constructor(props){
super(props);
this.state = {
name: '',
email: ''
}
}
handleChange(name, value){
let state = this.state;
state[name] = value;
this.setState({state});
}
render () {
return (
<section>
<Input type='text' label='Name' name='name' value={this.state.name} onChange={this.handleChange.bind(this, 'name')} maxLength={16} />
<Input type='email' label='Email address' icon='email' value={this.state.email} onChange={this.handleChange.bind(this, 'email')} />
</section>
);
}
I'm not sure how it works, but it pass name and value as params to the handleChange function, thus, you can get value as a second param. You don't need event.
First change your this.state in constructor to single - this.state = {emai:'',password:''}, then try to bind handleEmailChange and handlePasswordChange inside of input instead in constructor, u need to set this direct to input,
UPDATE
or if Input and Button are components, u need implement changeMethod and onChange event in them, and send value back to component Login via callback
HOW IT WORKS -
class Input extends React.Component{
constructor(props){
super(props);
this.state= {
value : this.props.value
}
}
componentWillReceiveProps(nextProps){
this.setState({
value: nextProps.value,
})
}
render(){
return(
<input onChange={this.handleChangeValue.bind(this)} type="text" name={this.props.name} value={this.state.value} placeholder={this.props.placeholder} className={**just class name or send via props too**} />
)
}
handleChangeValue(e){
this.setState({value:e.target.value});
this.props.changeValue(e.target.value);
}
}
class Login extends React.Component{
constructor(props){
super(props);
this.state= {
emailValue : '',
passwordValue: '',
...
}
}
render(){
return(
<div>
<Input type="text" name='email' value={this.state.emailValue} placeholder={'Write email'} className='someName' changeValue={this.changeEmailValue.bind(this)} />
<Input type="text" name='password' value={this.state.passwordValue} placeholder={'Write password'} className='someName' changeValue={this.changePasswordValue.bind(this)} />
</div>
)
}
changeEmailValue(value){
this.setState({emailValue:value});
}
changePasswordValue(value){
this.setState({passwordValue:value});
}
}
Since you are using a custom component <Input/>, it is likely whatever special code that component has is abstracting away the default event passed from the built-in component <input/>. (note the lower case "i") So maybe you need it to read:
handleEmailChange(value) {
this.setState({email: value});
}
handlePasswordChange(value) {
this.setState({password: value});
}
Regardless the fix is likely that onChange is not returning an event, but some other value you were not expecting.
Instead on binding events in the constructor bind it with the input fields, it will solve your problem.
one more suggestion: you are using two state object, Don't use two separate state variable initialisation, define it like this:
this.state = {
email: '',
password: '',
};
If you use Formik, You should set initialValues for it.
<Formik
initialValues={{
email: '',
password: '',
}}
onSubmit={(values) => {
console.log("formik", values)
}}
>

Categories

Resources