Input fields values taken state cannot be modified - javascript

I cannot modify input predefined value.
constructor(props) {
super(props);
this.state = {
userDetails: this.props.location.state.userDetails,
token: "",
firstName: "",
lastName: "",
email: "",
roles: []
};
This is the method that I use for handling input:
handleInputChange = (e) => {
const target = e.target;
const value = target.value;
const name = target.name;
this.setState = {
[name]: value,
};
};
This is the input element:
<input
type="text"
value={this.state.userDetails.firstName}
name="firstName"
ref="firstName"
onChange={this.handleInputChange}
/>
In the UI I cannot delete its(input) default value taken from the state neither type something else. If I comment the value this problem disappears.
What am I doing wrong in this case?

<input> element always reads its value from this.state.userDetails.firstName, which doesn't change.
Because of that, <input>'s value has to be firstName:
<input
type="text"
value={this.state.firstName}
...
Then you can, for instance, set firstName: this.props.location.state.userDetails.firstName as default value on first render.

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}

Javascript object elements is undefined

Trying to log the values of name, day and dob elements stored in dataEdited as object.Two of the elements display Undefined with just one displaying the correct value.
Here is the code
/* two-way state binding */
dataChange(event){
const target = event.target;
const value = target.value; //gets value of the textbox
const name = target.name;
this.setState({ dataEdited: {[name]: value} });
}
handleUpdate(event){
event.preventDefault();
const {name,day,dob} = this.state.dataEdited;
console.log(name, day, dob);
/* this.setState({ toggle: false }) */
}
State
this.state = {
name: '',
day: '',
dob: '',
items : [],
currentItem: {},
dataEdited: {},
toggle: false,
loading: false
}
Render
<form onSubmit={this.handleUpdate}>
<input
className=""
name="name"
onChange={this.dataChange}
defaultValue={this.state.currentItem.name}
placeholder= "Celebrant's Name"
ref={name => this.name = name}
required />
<input
className=""
type="number"
name="day"
min="1"
max="31"
ref={day => this.day = day}
onChange={this.dataChange}
defaultValue={this.state.currentItem.day}
placeholder= "day" />
<input
className=""
name="dob"
type="month"
onChange={this.dataChange}
defaultValue={this.state.currentItem.dob} />
<button type="submit">update</button>
<button onClick={this.handleEditCancel}>cancel</button>
</form>
This is the result on the console
undefined undefined "2020-08"
I don't understand how this is possible, can I get an explanation. Also, how can I fix this?
When you execute this.setState({ dataEdited: {[name]: value} }); you overwrite the other values in the object assigned to dataEdited.
You should change that to this.setState({ dataEdited: { ...this.state.dataEdited, [name]: value} }); to preserve the previous values inside this.state.dataEdited
UPDATE (Thanks #JMadelaine): You should use this.setState(prev => ({ dataEdited: { ...prev.dataEdited, [name]: value}})); to ensure that no concurrent state changes affects the setState()
More info: https://stackoverflow.com/a/50837784/10201813
Problem
you are overwriting the same dataEdited variable over and over when you call this.setState({ dataEdited: {[name]: value} });
Thus only the last changed data will be present inside dataEdited
Solution
Seperately save the data or change setState function appropriately
You are overwriting the state variable dataEdited everytime you call
this.setState({ dataEdited: {[name]: value} });
so you want to change spread the object dataEdited and the state After that add or change the name,dob or day
this.setState({
...this.state,
dataEdited: { ...this.state.dataEdited, [name]: value }
});
CodeSandbox here

Handling multiple inputs in React

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

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

Keep getting a controlled vs uncontrolled react error

I keep getting this error in my console:
warning.js?8a56:36 Warning: LoginForm is changing a controlled input
of type password 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: https://facebook.github.io/react/docs/forms.html#controlled-components
I have had a look at these questions:
How to create a controlled input with empty default in React 15 - I define my state already
React Controlled vs Uncontrolled inputs - My function takes an event and not a password parameter
React - changing an uncontrolled input - I already define the password as empty
Here is the component:
export default class LoginForm extends Component {
constructor(props) {
super(props);
this.state = {
values: {
username: "",
password: ""
}
};
this.onChange = this.onChange.bind(this);
this.onSubmit = this.onSubmit.bind(this);
this._clearInput = this._clearInput.bind(this);
}
onSubmit(event) {
// this.props.attemptLogin
event.preventDefault();
console.log(this.state.values);
let {username, password} = this.state.values;
this.props.attemptLogin(username, password)
.then((userInfo) => {
console.log(userInfo);
LocalStore.setJson('api', userInfo.api);
LocalStore.setJson('user', userInfo.user);
this._clearInput('username' , 'password');
})
.catch((err) => {
console.log("Failed:");
console.log(err);
this._clearInput('password');
});
}
_clearInput(...fields) {
let newValuesState = {};
fields.forEach((field) => {
newValuesState[field] = '';
});
this.setState({values: newValuesState});
}
onChange(event) {
let name = event.target.name;
let value = event.target.value;
this.setState({values: {[name]: value}});
}
render() {
return (
<div>
<form method="post" onSubmit={this.onSubmit}>
<div><input type="text" name="username" onChange={this.onChange} value={this.state.values.username}/></div>
<div><input type="password" name="password" onChange={this.onChange} value={this.state.values.password}/></div>
<div><button type="submit">Login</button></div>
</form>
</div>
);
}
}
LoginForm.propTypes = {
attemptLogin: React.PropTypes.func.isRequired
};
The code does seem to work but this error pops up in the console and so I can't continue I till I get it to stop appearing. Can anyone see what's I did wrong in the component?
Since you are nesting the values in state.values, your onChange function will remove the field not being changed. You could do something like this instead:
onChange(event) {
let name = event.target.name;
let value = event.target.value;
let values = {...this.state.values};
values[name] = value;
this.setState({values}});
}

Categories

Resources