Form input field value issue - javascript

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

Related

onSubmit function type in React and TypeScript

I'm having issues creating an `onSubmit function and giving it a type.
On the very first line where you'd normally set a type for the function, no matter what, I seem to give even any or unknown I'm seeing an error.
The error suggests I can't put the type of any on the type of
(event: FormEvent<HTMLFormElement>, data: FormProps) => void
But even if I do give it this exact suggested type, I still return an error.
const onSubmitHandler = (
e: SyntheticEvent<HTMLFormElement>,
setVerificationId: Dispatch<SetStateAction<string>>,
setVerificationToken: Dispatch<SetStateAction<VerificationStatus>>): TRYING_TO_RESOLVE_WHAT_THIS_TYPE_IS => {
e.preventDefault()
setUserData({
platform: '',
name: {
givenNames: givenNames ? givenNames : memberDetails.memberNameDetails.givenNames,
surname: surname ? surname : memberDetails.memberNameDetails.lastName
},
email: email,
dob: formattedDateOfBirth,
currentAddress: {
line1: streetNameInformation,
postCode: postCode,
suburb: suburb,
state: state,
country: country,
}})
aService.postVerificationRegister(userData)
.then(response => {
setVerificationId(response.verificationId)
setVerificationToken(response.verificationToken)
})
.catch((e) => console.error("There was an error getting a verification token", e))
}
<form id="not-the-form" role="form" action="/" onSubmit={onSubmitHandler}>
The submit handler function in React could be like the below in terms of type. Find a link to a live demo as well.
import { FormEvent } from "react";
function onSumbitHandler(e: FormEvent<HTMLFormElement>) {
// ...
}
<form onSubmit={onSumbitHandler}></form>
If your submit handler function needs other type of parameters, you can do it this way using an arrow function:
<form
onSubmit={(e) =>
onSubmitHandler(e, setVerificationId, setVerificationToken)
}
></form>
This should be a SyntheticEvent
I just pulled the semantic-ui-react library and tested. The following works with typescript (assuming you're using the 2nd argument).
import React from "react";
import { Form, FormProps } from "semantic-ui-react";
const Component = () => {
const handleSubmit = (e: React.FormEvent<HTMLFormElement>, data: FormProps) => {/* your code */};
return (
<Form onSubmit={handleSubmit} />
);
};
If you're not using the data argument then you'd have
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => { /* your code */};
The return type that you're thinking typescript is complaining about is automatically determined to be "void" by typescript because the function is not returning any value.
Edit: Your question appears to have changed, and your handler now has 3 arguments. Neither the html <form/> element or the semantic-ui-react <Form/> onSubmit function signatures match what you're passing. It sounds like you're wanting to pass custom arguments to your handler. In which case you'd need to have a higher order function.
const onSubmitHandler = (setVerificationId: type, setVerificationToken: type) = (e: React.FormEvent<HTMLFormElement>) => { /* your code */}
// calling code
<Form onSubmit={onSubmitHandler(setVerificationId, setVerificationToken)}>

React Form Field on change function not passing target value to setState

This code could use some improvement, but for now, is there a reason why the name property inside setState cannot access the event e parameter?
//keyObject is a predefined variable
<Form.Control
type="text"
value={this.state.productObject[keyObject].name || ""}
placeholder={"name"}
onChange={(e) => {
this.setState(function (prevState) {
return {
...prevState,
productObject: {
...prevState.productObject,
[keyObject]: {
name: e.target.value,
price: prevState.productObjecy[keyObject].price,
selected: prevState.productObject[keyObject].selected,
},
},
};
});
}}
/>;
When I attempt to change the value of the field, I get the 'Cannot read property 'value' of null' error.
Have you tried
e.target.name.value
An easy way to debug this is to do console.log(e.target) and see what you need next.

React Cannot read property 'name' of undefined on [event.target.name]

I have a 3rd-party component using Phone Input here https://github.com/bl00mber/react-phone-input-2#react-phone-input-2 I need to get the value input by the user and set the form state using the method below that handles onChange:
const handlePhoneChange = (event) => {
setFormState({
...formState,
[event.target.name]: event.target.value,
});
};
This is my configuration for the 3rd-party component below:
<PhoneInput
country={"us"}
error={hasError("phone")}
helperText={hasError("phone") ? formState.errors.phone[0] : null}
name="phone"
value={formState.values.phone || ""}
onChange={handlePhoneChange}
/>
However, I get the error TypeError: Cannot read property 'name' of undefined when I try to type a number in the phoneInput component. Kindly help me resolve this.
If you read the documentation for the onchange event, there are 4 arguments that are accessible: onChange(value, country, e, formattedValue).
The way you're accessing event is incorrect, because you're accessing the first positional arugment, which is the value of the input. You get an error trying to access event.target since it is not an Event object.
The Event object is available as the third positional argument, so if you use it as such, it should work:
const handlePhoneChange = (value, country, event) => {
setFormState({
...formState,
[event.target.name]: value,
});
};

Cannot read property of undefined React Hooks

I am trying to create a search feature using react hooks but it keeps returning the error:
Cannot read Property of Undefined
on the updateSearch function whenever I type in the input field.
const [search, setSearch] = React.useState('');
const [searchResults, setSearchResults] = React.useState([]);
const [state, setState] = React.useState({
open: false,
name: '',
users:[]
});
useEffect(() => {
getAllUsers();
}, []);
const getAllUsers = () => {
fetch('/userinformation/', {
method: 'GET',
headers: {'Content-Type':'application/json'}
})
.then(function(response) {
return response.json()
}).then(function(body) {
console.log(body);
setState({...state, users: body });
})
}
const updateSearch = (event) => {
setSearch(event.target.value)
}
React.useEffect(() => {
const results = state.users.filter(user =>
user.toLowerCase().includes(search)
);
setSearchResults(results);
}, [search]);
return (
<input type="text" value={search} onChange={(e) => updateSearch(e.target.value)}/>
)
Whenever I type in the search bar I get the following error:
How can i fix this?
You can either get to the value of passed event by changing
<input type="text" value={search} onChange={(event) => updateSearch(event}/>
or you can keep the input element as it is and change the update updateSearch callback to
const updateSearch = (event) => { setSearch(event) }
Secondly, you are applying includes to a single item of an array which is specific to array methods, you need to make following changes as well to make it work:
React.useEffect(() => {
const results = state.users.filter( user => user.firstName.toLowerCase() === search );
setSearchResults(results);
}, [search])
in your input you're already passing valueonChange={(e) => updateSearch(e.target.value) and in updateSearch you're trying to accessing it. Change it like this, if you want to access event in updateSearch method and get value from it.
<input type="text" value={search} onChange={(e) => updateSearch(e}/>
I would teach you a secret that has worked very well for me over the years. When javascript gives you such error as cannot read property ***whateverValue*** value of undefined it means you are trying to read the property of an object that does not exist. In this case, the object you're trying to read from is undefined, hence it cannot have any key: value pair.
Back to your question: TypeError: Cannot read property value of undefined
Using cmd+f to check for all places where value is used shows me everywhere you used value on event.target.value
Stay with me (I know this is boring, but it would help later).
You have an event handler named updateSearch.
All you need here now is to change your input tag to this:
<input type="text" value={search} onChange={updateSearch}/>
Don't worry, React would handle the rest, it automatically parses the event to eventHandler which can then be accessed on such eventHandler.
Also, I think you might want to refactor this component.
Something like import React, {useState, useEffect} from React
you won't have to call React.useEffect or React.useState in other parts of the project. just useEffect or useState.
Goodluck :+1
You have already passed the value of the input into the updateSearch method.
This is how you should fix it:
const updateSearch = (value) => {
setSearch(value);
};
And as for the second issue you have raised on your useEffect hook, you will have to call toLowerCase() on one of your properties (either firstName or lastName), depending on what you need to search for.
React.useEffect(() => {
const results = state.users.filter(user =>
user.firstName.toLowerCase().includes(search)
);
setSearchResults(results);
}, [search]);

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

Categories

Resources