generic event handler to set the state of any form field - javascript

Here we have to write three functions to change the state of all input fields of form and in case if we have more fields e.g phone and address we have to write two more functions to change the state of those fields, i want to ask is there any way we can write only a single generic function to change the state of any field of this form rather than write separate function for every form field?
class SignUpForm extends React.Component {
constructor() {
super();
this.state = {
name: '',
email: '',
password: '',
};
}
handleNameChange = (evt) => {
this.setState({name: evt.target.value});
}
handleEmailChange = (evt) => {
this.setState({email: evt.target.value});
}
handlePasswordChange = (evt) => {
this.setState({password: evt.target.value});
}
render(){
return(
<form onSubmit={this.handleSubmit}>
<input
type="text"
placeholder="Enter Name"
value={this.state.name}
onChange={this.handleNameChange}
/>
<input
type="text"
placeholder="Enter email"
value={this.state.email}
onChange={this.handleEmailChange}
/>
<input
type="password"
placeholder="Enter password"
value={this.state.password}
onChange={this.handlePasswordChange}
/>
<button disabled={isDisabled}>Sign up</button>
</form>
)
}
}

You could use the following pattern:
handleChange = (type, event) => {
this.setState({[type]: event.target.value});
}
You use handleChange like that:
<input
type="text"
placeholder="Enter Name"
value={this.state.name}
onChange={(event) => this.handleChange('name', event)}
/>

There's an even cleaner way to write this using currying:
handleChange = type => event => this.setState({[type]: event.target.value})
<input
type="text"
placeholder="Enter Name"
value={this.state.name}
id="name"
onChange={this.handleChange('name')}
/>
You could even use the element's ID attribute to avoid the instantiation of a new handler every time you render (since this.handleChange('name') would return a new instance of event => this.setState({name: event.target.value}) every render), and to avoid repeating yourself. However this may not be advisable since it means your element IDs must match your state keys.
handleChange = event => this.setState({[event.target.id]: event.target.value})
<input
type="text"
placeholder="Enter Name"
value={this.state.name}
id="name"
onChange={this.handleChange}
/>

Related

React Hooks Form: undefined value on submit

I'm trying to create separate input component and use React Hook Forms, Here is component:
export default function TextInput({label, id, placeholder, name, ...params}) {
return (
<FormGroup>
<Label htmlFor={id}> {label} </Label>
<Input
className="form-control"
placeholder={placeholder}
name={name}
id={id}
{...params}
/>
</FormGroup>
);
}
and I'm calling it like this:
const {register, handleSubmit} = useForm();
const onSubmit = (data) => {
console.log(data);
}
<form onSubmit={handleSubmit(onSubmit)}>
<TextInput
label="input password"
placeholder="password"
name="password"
{...register("password")}
id="password"
type="password"
/>
<button type="submit">submit</button>
</form>
But when I click submit, password seems to be undefined
React-hook-version is 7.33, any ideas about what is the problem?
lets say you passed the a varible to get value from Input Field.
and next step will be to give this input field a function so that it can update the value of that variable in your case passowrd.
to do that I will use onChange.
<input onChange={(event)=>{resister.password = event.target.value}}/>
or
<input onChange={(event)=>{resister['password'] = event.target.value}}/>
and according to useForm documentation
you should be using resister in <input/> not in <TextInput/>
const { onChange, onBlur, name, ref } = register('firstName');
// include type check against field path with the name you have supplied.
<input
onChange={onChange} // assign onChange event
onBlur={onBlur} // assign onBlur event
name={name} // assign name prop
ref={ref} // assign ref prop
/>
// same as above
<input {...register('firstName')} />
https://react-hook-form.com/api/useform/register

React onChange not updating state

I am creating a simple login form and I am saving the users information to state as they type. However, the value does not register to state as expected.
Here is the user state
const [user, setUser] = useState({
firstName: '',
lastName: '',
email: ''
});
Here is the input component
export function Input({ id, placeholder, autoComplete, type, name, label, value, handleInputChange }) {
return (
<div className="form-input">
<label className="form-label" htmlFor={id}>{label}</label>
<input
placeholder={placeholder}
autoComplete={autoComplete}
id={id}
type={type}
name={name}
value={value}
onChange={handleInputChange}
/>
</div>
)
}
Here is the handleInputChange function that is passed into the input component
function handleInputChange(event) {
const { name, value } = event.target;
setUser({ ...user, [name]: value });
}
Here is how one of the input components is used in the parent component
<Input
id="first-name"
placeholder="Charlie"
autoComplete="given-name"
type="text"
name="firstName"
label="First Name"
value={user.firstName}
handleInputChange={handleInputChange}
/>
Here are some resources I've looked at thus far:
https://reactjs.org/docs/hooks-reference.html#usestate
onChange not updating React
https://daveceddia.com/usestate-hook-examples/
I'd suggest to create one state per input field:
export function Form({ addItem }) {
const [name, setName] = useState('');
return (
<form>
<Input
id="first-name"
placeholder="Charlie"
autoComplete="given-name"
type="text"
name="firstName"
label="First Name"
value={user.firstName}
handleInputChange={(event) => setName(event.target.value)}
/>
</form>
);
}

Using handleChange function for several inputs

My desire is that the handleChange function should only affect the input value that you change. If i change the 'firstName' input i wish that only that state gets updated and same goes for the second input-field, 'lastName'. Suggestions on how this can be done are much appreciated.
StateObject
const [user, setUser] = useState({
firstName :"",
lastName: "",
});
Input
<input type="text" placeholder="Firstname" onChange={handleChange}/>
<input type="text" placeholder="Lastname" onChange={handleChange}/>
handleChange() function
const handleChange = (event) => {
setUser({...user, firstName:event.target.value})
}
This is some rough code as I'm not in a position to test it, but the theory should be the same, might need tweaking, you can use a property accessor to update your object. Just tweak the HTML slightly.
Input
<input type="text" placeholder="Firstname" name="firstName" onChange={handleChange}/>
<input type="text" placeholder="Lastname" name="lastName" onChange={handleChange}/>
handleChange() function
const handleChange = (event) => {
setUser({...user, [event.target.getAttribute('name')]:event.target.value})
}
const makeHandleChange = key => event => {
setUser({...user, [key]: event.target.value});
};
<input type="text" placeholder="Firstname" onChange={makeHandleChange('firstName')}/>
<input type="text" placeholder="Lastname" onChange={makeHandleChange('lastName')}/>
You can change your input and add another attribute of name, then access the name property of the event and pass that as the property name to the setUser(...) function.
Input:
<input type="text" placeholder="Firstname" name="firstName" onChange={handleChange}/>
<input type="text" placeholder="Lastname" name="lastName" onChange={handleChange}/>
handleChange() function
const handleChange = (event) => {
setUser({...user, [event.target.name]: event.target.value})
}
This should do what you're expecting - add name attribute to your input that matches with your state object that manages the values.
const MyForm = () => {
const [user, setUser] = useState({
firstName :"",
lastName: "",
});
const handleChange = (event) => {
setUser({ ...user, [event.target.name]: event.target.value })
}
return (
<form>
<input type="text" name="firstName" placeholder="Firstname" onChange={handleChange}/>
<input type="text" name="lastName" placeholder="Lastname" onChange={handleChange}/>
</form>
)
}
Hope this is helpful :)

Creating a generalised onChange() function for different inputs without the use of name tag

I'm trying to create a generalized handleChange() function for all my input fields. I have over 30 input fields, so there is not really an option to have one handleChange method for each input, as this would create tons of boilerplate code. In the example below I have tried to create a function that will handle any input.
For my project, I'm using Ant design, which does not seem to support the name attribute inside their form elements (e.g. InputNumber/Input, etc.)
Example of an InputNumber field:
<InputNumber name='myNumber' min={1} max={100000} placeholder='Enter number' value={this.state.myNumber} onChange={handleChange}/>
This is my attempt at writing a generalized onChange method. But it passes this error: Cannot read property 'name' of undefined
handleChange = (e) => {
this.setState({
[e.target.name]: e.target.value
});
This method would allow me to reach any selected input element and update that specific one. However, this does work due to the lack of a name attribute.
As suggested below, this approach works, but still leaves me without a generalized solution - a way to handle any input and map to the correct state value.
state = {
myNumber: 0,
myNumber2: 0,
myNumber3: 0
};
handleChange = e => {
this.setState({
myNumber: e
});
};
In the example below the handleChange function will only update myNumber, not myNumber2 and myNumber3.
<InputNumber name='myNumber' min={1} max={100000} placeholder='Enter number' value={this.state.myNumber} onChange={handleChange}/>
<InputNumber name='myNumber2' min={1} max={100000} placeholder='Enter number' value={this.state.myNumber2} onChange={handleChange}/>
<InputNumber name='myNumber3' min={1} max={100000} placeholder='Enter number' value={this.state.myNumber3} onChange={handleChange}/>
Any suggestions to a workaround?
According to InputNumber API, onChange accepts a Number|String, e.target.value will always resolve run time error.
onChange: The callback triggered when the value is changed - function(value: number | string)
Here is an example, where I suppose in writing:
<InputNumber name='myNumber' min={1} max={100000} placeholder='Enter number' value={this.state.myNumber} onChange={handleChange}/>
You meant using a class component, because onChange={handleChange} as you described must be a part of a class.
export default class App extends React.Component {
state = {
myNumber: 0
};
handleChange = e => {
this.setState({
myNumber: e
});
};
render() {
return (
<FlexBox>
<InputNumber
name="myNumber"
min={1}
max={100000}
placeholder="Enter number"
value={this.state.myNumber}
onChange={this.handleChange}
/>
</FlexBox>
);
}
}
If you want to use a generic method handleChange you should change the component type from InputNumber to Input, that's because you want to access name="myNumber" property.
handleChange = e => {
e.persist();
this.setState({
[e.target.name]: e.target.value
});
};
A better approach keeps using InputNumber and implement handleChange as curried function:
handleChange = name => e => {
this.setState({
[name]: e
});
};
Check the example of both approaches:
class AppInputNumber extends React.Component {
state = {
myNumber: 0,
myNumber2: 0,
myNumber3: 0
};
handleChange = name => e => {
this.setState({
[name]: e
});
};
render() {
return (
<FlexBox>
<InputNumber
min={1}
max={100000}
placeholder="Enter number"
value={this.state.myNumber}
onChange={this.handleChange('myNumber')}
/>
<InputNumber
min={1}
max={100000}
placeholder="Enter number"
value={this.state.myNumber2}
onChange={this.handleChange('myNumber2')}
/>
<InputNumber
min={1}
max={100000}
placeholder="Enter number"
value={this.state.myNumber3}
onChange={this.handleChange('myNumber3')}
/>
</FlexBox>
);
}
}
export default class App extends React.Component {
state = {
myNumber: 0,
myNumber2: 0,
myNumber3: 0
};
handleChange = e => {
e.persist();
this.setState({
[e.target.name]: e.target.value
});
};
render() {
return (
<FlexBox>
<Input
name="myNumber"
min={1}
max={100000}
type="number"
placeholder="Enter number"
value={this.state.myNumber}
onChange={this.handleChange}
/>
<Input
name="myNumber2"
min={1}
max={100000}
type="number"
placeholder="Enter number"
value={this.state.myNumber2}
onChange={this.handleChange}
/>
<Input
name="myNumber3"
min={1}
max={100000}
type="number"
placeholder="Enter number"
value={this.state.myNumber3}
onChange={this.handleChange}
/>
<AppInputNumber />
</FlexBox>
);
}
}
I am not sure but if the e is an event object, try this
e.target.getAttribute('name')

Reset form input values in React

I want create a function using with i can reset value in form inputs without submit. I tried create that function in App Component (resetFormFields) and pass it on props to Form Component. It's preety simply when I want to do this onSubmit (e.target.reset()) but I got stuck when I have to do it without submit, on a different element than the form. Can I do that without adding these values to state?
App:
class App extends Component {
state = {
people: [],
formMessages: [],
person: null
};
handleFormSubmit = e => {
e.preventDefault();
const form = e.target;
const name = form.elements["name"].value;
const username = form.elements["username"].value;
this.addPerson(name, email);
form.reset();
};
resetFormFields = () => {
return;
}
render() {
return (
<div className="App">
<Form formSubmit={this.handleFormSubmit}
reset={this.resetFormFields} />
</div>
);
}
Form:
const Form = props => (
<form className={classes.Form}
id="form"
onSubmit={props.formSubmit}>
<input autoFocus
id="name"
type="text"
defaultValue=""
placeholder="Name..."
/>
<input
id="email"
type="text"
defaultValue=""
placeholder="Email..."
/>
<Button
btnType="Submit"
form="form"
type='submit'>
Submit
</Button>
<label onClick={props.reset}>Reset fields</label>
</form> );
onHandleFormSubmit = (e) =>{
e.preventDefault();
e.target.reset();
}
You need to make your inputs controlled by passing the value you store in your state then you just have to reset the state values and your component value resets.
check this sample below
handleInputChange = (e) => {
let { name, value } = e.target;
this.setState({
...this.state,
inputs: {
[name]: value
}
});
}
your component will now look like
<input name='fullName' value={this.state.inputs.fullName} onChange={this.handleInputChange} />
Your reset function will just clear the state and your input field will be empty since it's controlled via state
resetInputFields = () => {
this.setState({ inputs: {} })
}
you should give set your input values based on component state, then just update the component state
class App extends Component {
state = {
people: [],
formMessages: [],
person: null,
name: "",
email: "",
};
updateState = (newState) => {
this.setState(newState);
}
handleFormSubmit = e => {
e.preventDefault();
this.addPerson(this.state.name, this.state.email);
form.reset();
};
resetFormFields = () => {
this.setState({name:"", email: ""});
}
render() {
return (
<div className="App">
<Form formSubmit={this.handleFormSubmit} updateState={this.updateState}
reset={this.resetFormFields} email={this.state.email} name={this.state.name} />
</div>
);
}
and then
const Form = props => (
<form className={classes.Form}
id="form"
onSubmit={props.formSubmit}>
<input autoFocus
id="name"
type="text"
defaultValue=""
value={this.props.name}
onChange={(e) => this.props.updateState({name: e.target.value})}
placeholder="Name..."
/>
<input
id="email"
type="text"
defaultValue=""
value={this.props.email}
onChange={(e) => this.props.updateState({email: e.target.value})}
placeholder="Email..."
/>
<Button
btnType="Submit"
form="form"
type='submit'>
Submit
</Button>
<label onClick={props.reset}>Reset fields</label>
</form> );

Categories