Handling multiple inputs in React - javascript

I am new to React and was learning how to handle multiple inputs in form. Here is the code :
class Login extends Component {
constructor () {
super();
this.state = {
email: '',
password: ''
};
this.handleChange = this.handleChange.bind(this);
}
handleChange (evt) {
// check it out: we get the evt.target.name (which will be either "email" or "password")
// and use it to target the key on our `state` object with the same name, using bracket syntax
this.setState({ [evt.target.name]: evt.target.value });
}
render () {
return (
<form>
<label>Email</label>
<input type="text" name="email" onChange={this.handleChange} />
<label>Password</label>
<input type="password" name="password" onChange={this.handleChange} />
</form>
);
}
}
The question is how can this.setState({ [evt.target.name]: evt.target.value }); replace several handlers? Does [evt.target.name] represent both inputs?

[evt.target.name] doesn't necessarily represent both inputs, it merely takes the name of the event's target and makes it the key for setState.
This means that when the email form changes, the this.setState will act as follows:
this.setState({ email: evt.target.value });
For the password this works the same;
this.setState({ password: evt.target.value });
So it doesn't necessarily replace several handlers, it mostly replaces them and supplies a shorter way to handle the event. (Think DNRY (Do Not Repeat Yourself)).
However many fields you have in the form, this.setState({ [evt.target.name]: evt.target.value }); will behave as explained above.
To further elaborate, in your current form, with a field for the email and a field for the password, the following will happen;
handleChange (evt) {
this.setState({ [evt.target.name]: evt.target.value });
}
Above function will take the event's target and extract the name and value attributes. So for EACH input with this change handler it'll change the function to i.e. the following if the email gets changed;
handleChange (evt) {
this.setState({ email: "email#domain.com" });
}
OR i.e. for the password
handleChange (evt) {
this.setState({ password: "superstrongpassword" });
}
OR i.e. for the name
handleChange (evt) {
this.setState({ name: "John Doe" });
}
Hope this helps!

This is basic example.
class Login extends Component {
constructor () {
super();
this.state = {
email: '',
password: ''
};
this.handleChange = this.handleChange.bind(this);
}
handleChange (evt, field) {
// check it out: we get the evt.target.name (which will be either "email" or "password")
// and use it to target the key on our `state` object with the same name, using bracket syntax
this.setState({ [field]: evt.target.value });
}
render () {
return (
<form>
<label>Email</label>
<input type="text" name="email" onChange={(event)=>this.handleChange(event, "email")} />
<label>Password</label>
<input type="password" name="password" onChange={(event)=>this.handleChange(event, "password")} />
</form>
);
}
}

This works because evt is a change event, evt.target is the changed DOM element and evt.target.name is the value of the name attribute of that element.
This means that when you change one of the <input> elements the handleChange function is called and the state's email or password (the names of those two inputs) property is changed to the changed element's value.
{[variable]: value} is just the syntax you use when you want to use a string as a property name in an object literal.

Every input has a name attribute which is used to reference elements in JavaScript, or to reference form data after a form is submitted. Here you are using name and password as names.
this.setState({ [evt.target.name]: evt.target.value })
This is using ES6 Computed property names.
evt.target.name takes the name of the input (i.e the evt.target) to which the handler is attached and being called and sets it as the key for your state. So, essentialy each time you change something in the input, your state corresponding to that input's name changes as well.
Keep one thing in mind. You need to keep the initial state names and the names of your input consistent with each other for this to work.
You can always write seperate handlers but that just convolutes the codebase and are essentially doing the same thing. So it is just following the DRY methodology. Might as well use this.
Hope this helps!

for create a state for this, is necessary create a object with any property and set
just behaivor on component an above class with your scheme class and component this is trigger for you response
Create an object property
state = {
nameForProperty: '',
anotherNamenameForProperty: ''
}
create an event for setState a pass event for function callback
onHandleEvent = e => {
this.setState ({
[e.target.name]: e.target.value,
})
console.log(this.setState)
};
Call function from input
<form>
<input name="namenameForProperty" onChange={this.onHandleEvent} value={this.state.} />
</form>

You can try something like this.
handleChange (evt) {
let name = evt.target.name
this.setState({ [name ]: evt.target.value });
}

Well, you can also adopt chained arrow function handleChange as handleChange = key => e => { ... } , then use it in input as onChange={this.handleChange('mail')}.
constructor(ops) {
super(ops)
this.state={
prompt: '',
form: {
name: '',
mail: '',
message: ''
}
}
}
handleChange = key => e => {
let newForm = {
...this.state.form,
[key]: e.target.value
}
this.setState({
form: newForm
})
}
render() {
let { name, mail, message } = this.state.form
return (
<form action="#">
<input type="text" value={name} onChange={this.handleChange('name')} className="form-control" placeholder="Name*" />
<input type="email" value={mail} onChange={this.handleChange('mail')} className="form-control" placeholder="E-mail*" />
<textarea value={message} onChange={this.handleChange('message')} className="form-control" placeholder="Message"></textarea>
</form>
)
}

Related

dynamically setting of a property name of an object

I have four input fields of an html form which the name attributes are extracted using the event handler onChangeHandler. these names are extracted using e.target.name . Now here is the issue, I want to update the state object by copying an initialState object into the state object but making sure the current input experiencing the onChange event is also updated. using e.target.name to set the property name does not work, but [e.target.name] works the magic.
why is this so? why does [e.target.name] work instead of e.target.name?
here is the code
const initialState = {
displayName : '',
email : '',
password : '',
password_confirmation: ''
}
function SignUpForm(){
const [fieldState , setFieldState] = useState(initialState);
const {displayName , email , password , password_confirmation} = fieldState;
const onChangeHandler = (e) => {
console.log(e)
//updating state when it is an object
//https://beta.reactjs.org/learn/updating-objects-in-state
setFieldState({
...fieldState , //Copy the initial state
[e.target.name] : e.target.value //but update this property
})
}
return(
<form>
<input type="text" name="displayName" onChange={onChangeHandler} value={displayName}/>
<input type="email" name="email" onChange={onChangeHandler} value={email}/>
<input type="password" name="password" onChange={onChangeHandler} value={password}/>
<input type="password" name="password_confirmation" onChange={onChangeHandler} value={password_confirmation}/>
</form>
);
}
export default SignUpForm```
In General, thats the way of adding dynamic property to an object, you need to wrap the prop. name with square brackets([]).
let obj = {
name: 'ray'
};
obj['age'] = 20; // obj = {name: 'ray', age: 20};
To access object property Dot(.) operator can use.
Javascript allows you to put an expression in brackets [], that will be computed and used as the property name.
const param = "size";
const config = {
[param]: 12
};
console.log(config); // {size: 12}

Form input field value issue

I am trying to input email value from user and store it in a state variable. I have tried the following code but getting an error:
<Form.Control type="email" required placeholder="Enter email" value={this.state.email} onChange={this.setEmail}></Form.Control>
State variable is defined as follow:
this.state = {
email: '',
}
Following is my code to set the value entered by user in email:
setEmail = (e) => {
this.setState(() => ({email: e.target.value}))
}
I tried printing e.target.value. It is correct. But while running this code, I am getting error:
Cannot read property 'value' of null
Any comments on how to fix this issue?
That is happening because the event is not persisted, and it is used in an asynchronous function (setState is async).
Just extract it before calling setState.
setEmail = (e) => {
const {value} = e.target;
this.setState(() => ({email: value}))
}

How does the React component's state changes by using [event.target.name]?

I am having an update form to update the user's info.
const { basic: userBasic } = this.state.user;
<Modal open={openEditBasicModal} size="small">
<Modal.Header>Your basic details</Modal.Header>
<Modal.Content scrolling>
<Form>
<Form.Group inline widths="equal">
<Form.Input
fluid
type="text"
name="firstName"
defaultValue={userBasic.firstName}
onChange={this.handleBasicFromInputValue}
label="First Name"
/>
<Form.Input
fluid
type="text"
name="lastName"
defaultValue={userBasic.lastName}
onChange={this.handleBasicFromInputValue}
label="Last Name"
/>
</Form.Group>
</Form>
</Modal.Content>
<Modal.Actions open={true}>
<Button
primary
onClick={this.updateUserBasicInfo}>
Update
</Button>
</Modal.Actions>
</Modal>
The component's state is:
this.state = {
user: {
basic: {
firstName: null,
lastName: null,
email: null
},
//others
}
}
The default value in the form is from the state and the logic is to update the state by using onChange handler on the forms fields. So I used the one simple handler for this:
handleBasicFromInputValue = event => {
this.setState({
user: {
basic: {
[event.target.name]: event.target.value
}
}
});
};
The above handler DID update the state, BUT it also remove the state schema declared in the component. If the user state was having
user: {
basic: {
firstName: "theCoder",
lastName: "lastCoder",
email: "something#gmail"
}
}
and now I update the firstName field in the form, the state gets changed to
user: {
basic: {
firstName: "theCoderUpdated",
}
}
If I print the user state now on form submit:
updateUserBasicInfo = () => {
console.log(this.state.user.basic);
}
It only prints object containg firstName: "theCoderUpdated"
All other state variables have vanished. Shouldn't the other state variables remain unchanged?
I think this is the [event.target.name] which is causing the issue, but I am not sure why.
How does the [event.target.name] works behind the scene? And what would be the right way to do this state change?
React only does shallow state merging. You can read about it here:
https://reactjs.org/docs/state-and-lifecycle.html#state-updates-are-merged
That means that all when you call setState, all the top level properties are replaced completetly. For you, that means that user is completely replaced with the new user-object that you send in with setState. You can solve this in two ways. Either you flatten your state object, and have firstName, lastName, email in your state like this:
this.state = {
firstName: null,
lastName: null,
email: null
}
or you have to update your state like this:
handleBasicFromInputValue = event => {
this.setState({
user: {
basic: {
...this.state.user.basic,
[event.target.name]: event.target.value
}
}
});
};
You are replacing the basic object. Do it like this to keep the previous props
const shallowCopy = Object.assign({}, this.state.user);
shallowCopy.basic[event.target.name] = event.target.value
this.setState({user : shallowCopy});
You are replacing the basic object. you can do it like .
this.state.user.basic[event.target.name] =event.target.value;
this.setState({user : this.state.user});
You replace the state with the new one when assigning the firstName, as you are going beyond shallow copy. You have to do it like this:
handleBasicFromInputValue = (event) => {
var temp = this.state.user;
temp.basic[event.target.name] = event.target.value;
this.setState({
user: temp
});
};
I think your problem is when you create the new state that you don't maintain the old values.
Try this:
handleBasicFromInputValue = event => {
const { user } = this.state;
this.setState({
user: {
...user,
basic: {
...user.basic,
[event.target.name]: event.target.value
}
}
});
};
When you do "setState" you are creating a new state so that's when you change what you want but you must also maintain what you already had.
You can see that in React docs
With spread operator you are making a copy of what you already had and you add what you want to change.
PS: what you want to change always behind what you already had because otherwise it is possible to replace it since you are making a copy of what was before.
I hope I helped you :)

Generic function in react with multiple state variables

How to write a generic function with various state variable change based on the dropdown.
for ex:
I have dropdown 1 and dropdown 2. If I change the
dropdown 1, I need to change the few state variables like a, b, c
dropdown 2, I need to change the few state variables like x, y, z
I can do this with 2 functions. But how to write a a generic function for this?
handleChange: function(e) {
//api call to xyz.com/getcompany/<company id> to get the job list here
this.setState({
selectedCompany: e.target.value,
companychange: "company changed. New value: " + e.target.value
})
},
handleChange2: function (e) {
// api call to xyz.com/jobstatus/<job id> to get the job status\(like how many position available for this job\) here
this.setState({
jobchange:"job changed. New value " + e.target.value
})
}
Codepen: https://codepen.io/asdhanapal/pen/WmwJPj?editors=0011
You could use a curried function to simplify the code a bit:
changeToState(fn) {
return e => this.setState(fn(e));
}
render() {
//...
<input onChange={changeToState(e => ({ selectedCompany: e.target.value, /*...*/ }))} />
<input onChange={changeToState(e => ({ jobChange: e.target.value, /*...*/ }))} />
}
If that is still to much boilerplate, you could extract the handleChange event into a functional component:
const StateChanger = ({ action, parent, ...props }) => (
<input onChange={e => parent.setState(action(e))} {...props} />
);
// inside a component's render:
<StateChanger parent={this} action={e => ({ selectedCompany: e.target.value })} style={{ color: red }} />
but as I already mentioned in the comments, that might remove repeated code a bit, but it doesn't improve readability / maintainability whatsoever.
You can use below snippet:
handleChange = event => {
const target = event.target;
const value = target.type === 'checkbox' ? target.checked : target.value;
const name = target.name;
this.setState({
[name]: value
});
};
Reference to react docs section:
https://reactjs.org/docs/forms.html#handling-multiple-inputs
You can use following:
handleChange = event => {
const { name, value } = event.target;
this.setState({
[name]:value
})
}
// in input
<input name="username" onChange={this.handleChange} />
Try this:
handleChange: function(source, e) {
switch(source) {
case 'company':
//Need to do api call to get the job list here
this.setState({
selectedCompany: e.target.value,
companychange: "company changed. New value: " + e.target.value
})
break;
case 'job':
// Need to do api call to get the job status\(like how many position available for this job\) here
this.setState({
jobchange:"job changed. New value " + e.target.value
})
break;
};
},
<select value={this.state.selectedCompany} onChange={this.handleChange.bind(this, 'company')}>
<select value={this.state.selectedCompany} onChange={this.handleChange.bind(this, 'job')}>
As I read your description of the requirements, there are no functional dependencies between dropdown 1 and dropdown 2, so I'd split this up in two separate components.

React this.setState With Arrow Function Causes Error In Console

I have a very simple form where I'm storing a users email in the component's state and updating the state with an onChange function. There is a strange thing that is occurring where if my onChange function updates the state with a function I get two errors in the console whenever I'm typing. If I update the state with an object, however, I get no errors. I believe updating with a function is the recommended method so I'm curious to know why I'm getting these errors.
My Component:
import * as React from 'react';
import { FormGroup, Input, Label } from 'reactstrap';
interface IState {
email: string;
}
class SignUpForm extends React.Component<{}, IState> {
constructor(props: {}) {
super(props);
this.state = {
email: ''
};
}
public onEmailChange = (event: React.ChangeEvent<HTMLInputElement>) => {
this.setState(() => ({ email: event.currentTarget.value }))
};
// Using this function instead of the one above causes no errors
// public onEmailChange = (event: React.ChangeEvent<HTMLInputElement>) => {
// this.setState({ email: event.currentTarget.value })
// };
public render() {
return (
<div>
<h1>Sign Up</h1>
<div className='row' style={{ paddingTop: '20px' }}>
<div className='col-lg-4 offset-lg-4'>
<form>
<FormGroup style={{ textAlign: 'left' }}>
<Label>Email</Label>
<Input
value={this.state.email}
onChange={this.onEmailChange}
type='text'
placeholder='Email Address'
/>
</FormGroup>
</form>
</div>
</div>
</div>
);
}
}
export default SignUpForm;
The error messages I get are:
index.js:2178 Warning: This synthetic event is reused for performance reasons. If you're seeing this, you're accessing the method `currentTarget` on a released/nullified synthetic event. This is a no-op function. If you must keep the original synthetic event around, use event.persist(). See react-event-pooling for more information.
index.js:2178 Warning: A component is changing a controlled input of type text to be uncontrolled. Input elements should not switch from controlled to uncontrolled (or vice versa). Decide between using a controlled or uncontrolled input element for the lifetime of the component. More info: react-controlled-components
If your state update is derived from what is currently in your state (e.g. incrementing a count variable) you should use the update function version of setState.
If you are just setting a completely new value like you do with an event handler, you don't need to use the update function version. The commented out version in your question is perfectly fine.
If you want to use the update function version you must either use event.persist() so that you can use the event asynchronously, or simply extract the value before you call setState.
public onEmailChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const { value } = event.currentTarget;
this.setState(() => ({ email: value }))
};
You can't use event or any of its descendant properties once the event handler has terminated. Instead, you have to grab the value, then use the function (or if you prefer, use persist, as Tholle points out):
public onEmailChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const { value } = event.currentTarget;
this.setState(() => ({ email: value }));
};
That said, since you're not updating state based on state or props, this is one of the few situations where using the non-callback version of setState is fine (details):
public onEmailChange = (event: React.ChangeEvent<HTMLInputElement>) => {
this.setState({ email: event.currentTarget.value });
};

Categories

Resources