Using handleChange function for several inputs - javascript

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

Related

How to clean input fields after another operations in a function in React?

I have a button and 2 input field and I am sending these input field values to backend. etc doing some operations. After doing operations in addCustomer function, I want to reset input fields but it is not working.
Here is the code:
function TableFooterPanel(props) {
const [firstName, setFirstName] = useState('');
const [lastName, setLastName] = useState('');
const addNewCustomer = async (name, surname) => {
await service.addCustomer(name, surname);
props.funcParam();
setFirstName('');
setLastName('');
}
var isButtonDisabled = false;
(firstName.length <= 3 || lastName.length <= 3) ? isButtonDisabled = true : isButtonDisabled = false;
return (
<>
<Card className='buttonFooter'>
<Form className='buttonFooter'>
<input type="text" placeholder="First Name" defaultValue={firstName} onChange={e => setFirstName(e.target.value)}></input>
<input type="text" placeholder="Last Name" defaultValue={lastName} onChange={e => setLastName(e.target.value)}></input>
<Button disabled={isButtonDisabled} onClick={() => addNewCustomer(firstName, lastName)} className='addButton'>Add</Button>
<label hidden={!isButtonDisabled} className='labelStyle'>Field lengths must be at least 4 character</label>
</Form>
</Card>
</>
);
}
export default TableFooterPanel;
Here everything is working good except
setFirstName('');
setLastName('');
they are not resetting or setting to another value. What is the reason for that and how can I achieve it ?
The problem is that you're setting the defaultValue prop and not the value prop.
<input type="text" placeholder="First Name" value={firstName} onChange={e => setFirstName(e.target.value)} />
<input type="text" placeholder="Last Name" value={lastName} onChange={e => setLastName(e.target.value)} />
From docs:
https://reactjs.org/docs/forms.html#controlled-components
Maybe you could use a state hook to track the server response.
If valid response then send back true and trigger reset text-fields function
Without seeing your service code, I am not sure I can 100% guarantee any of this advice. But it seems to me that you should be doing that work in a .then() or a .finally()
for example:
const addNewCustomer = async (name, surname) => {
await service.addCustomer(name, surname)
.then(() => {
props.funcParam();
setFirstName('');
setLastName('');
})
};
You could also do this in a .finally if you want to to execute regardless of the results from the server.

Warning: A component is changing a controlled input to be uncontrolled in React js

HELP. i use react hook, i got the error when i type the input.
error:
Warning: A component is changing a controlled input to be uncontrolled. This is likely caused by the value changing from a defined to undefined, which should not happen. Decide between using a controlled or uncontrolled input element for the lifetime of the component.
Code:
const AddUser = () => {
const initialUserState = {
id: null,
name: '',
age: 0
}
const [users, setUsers] = useState(initialUserState)
const handleChange = (e) => {
setUsers({ [e.target.name]: e.target.value })
e.preventDefault()
}
return (
<div>
<input name="name" type="text" value={users.name} onChange={handleChange}/>
<input name="age" type="number" value={users.age} onChange={handleChange}/>
</div>
)}
This page and this paragraph can help you understand the problem.
When you get the updated input through the onChange event listener you don't need to pass again the data through the value attribute.
When data is passed through the value attribute the component is considered "controlled". This means that the component is controlled by your code and shouldn't receive user input.
If you just want to set a default value you can use the defaultValue attribute.
To remove the warning just remove the value={/* Something */}.
Unlike setting the state in a class component, useState doesn't merge the object you pass, and you have to do it manually when setting the state. When you set age, for example, you replace the entire state object, which makes name to be undefined, and vice versa.
Use functional update, and create a new state based on the previous state object before setting it.
const { useState } = React
const AddUser = () => {
const initialUserState = {
id: null,
name: '',
age: 0
}
const [users, setUsers] = useState(initialUserState)
const handleChange = (e) => {
// create the new state and set it
setUsers(prev => ({ ...prev, [e.target.name]: e.target.value }))
e.preventDefault()
}
return (
<div>
<input name="name" type="text" value={users.name} onChange={handleChange}/>
<input name="age" type="number" value={users.age} onChange={handleChange}/>
</div>
)
}
ReactDOM.render(
<AddUser />,
root
)
<script crossorigin src="https://unpkg.com/react#17/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#17/umd/react-dom.development.js"></script>
<div id="root"></div>
in my case, i figured out changing input atribute name from:
<div>
<input name="name" type="text" value={users.name} onChange={handleChange}/>
<input name="age" type="number" value={users.age} onChange={handleChange}/>
</div>
to:
<div>
<input name="fullname" type="text" value={users.fullname} onChange={handleChange}/>
<input name="age" type="number" value={users.age} onChange={handleChange}/>
</div>
for example in this i change name for fullname, due to react does not recognize it as a constant but as the attribute of the input

Why event target not passing through multiple react functions?

So I am trying to make a textfield component in React that is highly reusable but whenever I try to access event.target.name or event.target.value I get empty data.
Is there any way to get this code to work?
function LoginForm() {
const [form, setValues] = useState({
username: "",
password: ""
});
const printValues = e => {
e.preventDefault();
console.log(form.username, form.password);
};
const updateField = e => {
setValues({
...form,
[e.target.name]: e.target.value
});
};
const TextField = props => {
return(
<label>
React Component:
<input
value={props.value}
name='react'
onChange={e => props.change(e)}
/>
</label>
)
}
return (
<form onSubmit={printValues}>
<TextField
value={form.username}
name='username'
change={updateField}
/>
<br />
<TextField
value={form.password}
name='password'
change={updateField}
/>
<br />
<button>Submit</button>
</form>
);
}
This code is an example that I have gotten to work. Why does this code work but not the code above?
function LoginForm() {
const [form, setValues] = useState({
username: "",
password: ""
});
const printValues = e => {
e.preventDefault();
console.log(form.username, form.password);
};
const updateField = e => {
setValues({
...form,
[e.target.name]: e.target.value
});
};
return (
<form onSubmit={printValues}>
<label>
Username:
<input
value={form.username}
name="username"
onChange={updateField}
/>
</label>
<br />
<label>
Password:
<input
value={form.password}
name="password"
type="password"
onChange={updateField}
/>
</label>
<br />
<button>Submit</button>
</form>
);
}
your child component has it's name prop hardcoded name='react' and that's because your [e.target.name]: e.targe.value statement is not working, use name={props.name} instead and it would solve the problem.
const TextField = props => {
return(
<label>
React Component:
<input
value={props.value}
name={props.name}
onChange={e => props.change(e)}
/>
</label>
)
}
The first code you show has a prop change instead of the onChange event listener in the TextField component.
I was looking here https://material-ui.com/es/components/text-fields/ because I never used material-ui, and in the example they still use the regular onChange.
So try changing change for onChange, as it is the only difference between both code examples besides the use of the component.

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

generic event handler to set the state of any form field

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

Categories