React onChange not updating state - javascript

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

Related

'A component is changing a controlled input to be uncontrolled' and partial state update

I have a parent component that is interested in gathering a list of contacts (name+email):
import { useState } from 'react'
import AddContactFn from './components/AddContactFn'
function App() {
const [contacts, setContacts] = useState([])
const addNewContact = (newContact) => {
setContacts([...contacts, newContact])
}
return (
<div>
<AddContactFn newContact={addNewContact} />
</div>
)
}
export default App
through a child component, which renders a form, with two inputs fields and an 'Add' button:
import React from 'react'
import { useState } from 'react'
export default function AddContactFn({ newContact }) {
const [contact, setContact] = useState({name: ' ', email: ' '})
const addContact = (e) => {
e.preventDefault()
if (contact.name === ' ' || contact.email === ' ') {
return
}
newContact(contact)
}
return (
<div>
<h2>Add Contact</h2>
<form onSubmit={addContact}>
<label>Name</label>
<input
type='text'
name='name'
value={contact.name}
placeholder='Name'
onChange={e => setContact({ name: e.target.value })}
/>
<label>Email</label>
<input
type='text'
name='email'
value={contact.email}
placeholder='Email'
onChange={e => setContact({ email: e.target.value })}
/>
<button type='submit'>Add</button>
</form>
</div>
)
}
I am facing two issues:
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.
with both input fields containing text, every time the Add button is pressed, only the last edited value is returned to the parent (ie. either 'name' or 'email' is returned, not both)
Lets fix one at a time
The warning:
Try adding a check for the input value like value={contact.name || ''}
The OR || operator will check if contact.name is valid, if so use it otherwise ''
This is a side effect linked to the second issue
The state:
Your onChange handler needs to add the previous object properties, currently they are not, so try onChange={e => setContact(prevState, ({...prevState, name: e.target.value }))}
By doing onChange={e => setContact({ name: e.target.value })} you are losing all the properties of the state object, and setting the state to only have a name or email property, that is why you get the warning since the name or email are lost.
Since you have two input fields (name & email) you will have to do the same on both:
<label>Name</label>
<input
type='text'
name='name'
value={contact.name || ''}
placeholder='Name'
onChange={e => setContact(prevState => ({...prevState, name: e.target.value}))}
/>
<label>Email</label>
<input
type='text'
name='email'
value={contact.email || ''}
placeholder='Email'
onChange={e => setContact(prevState => ({...prevState, email: e.target.value}))}
/>
When you use setContact({ name: e.target.value }) (or for the email) you are also setting the other field as undefined. The way I would recommend you did this is:
import React from 'react'
import { useState } from 'react'
export default function AddContactFn({ newContact }) {
const [name, setName] = useState(' ');
const [email, setEmail] = useState(' ');
const addContact = (e) => {
e.preventDefault()
if (name === ' ' || email === ' ') {
return
}
newContact({ name, email })
}
return (
<div>
<h2>Add Contact</h2>
<form onSubmit={addContact}>
<label>Name</label>
<input
type='text'
name='name'
value={name}
placeholder='Name'
onChange={e => setName(e.target.value)}
/>
<label>Email</label>
<input
type='text'
name='email'
value={email}
placeholder='Email'
onChange={e => setEmail(e.target.value)}
/>
<button type='submit'>Add</button>
</form>
</div>
)
}
If you want to keep the state as it currently is then you can just do e.g. setContact({ ...contact, name: e.target.value })
You are using Controlled Input where you bind the input tag value to one of contact state property and the onChange event to an arrow function that will update the contact state.
Initially, the contact state has two properties, which are name, and email.
const [contact, setContact] = useState({name: ' ', email: ' '})
...
return (
<input
value={contact.name}
onChange={e => setContact({ name: e.target.value })}
/>
<input
value={contact.email}
onChange={e => setContact({ email: e.target.value })}
/>
)
When the onChange event occurs, it will update the contact state with a new object that only has one property. Here, React will raise a warning as one of the input values detects the change and found that the binding reference is missing/undefined. At that moment, your input has become an Uncontrolled Input. That's why you got this 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.
To fix it, you can clone the contact by using a shallow copy and update your setter like this:
onChange={e => setContact({ ...contact, name: e.target.value })}
onChange={e => setContact({ ...contact, email: e.target.value })}
So, whenever onChange event occurs, the updated contact state will always have two properties both name, and email. Of course, you can use the other technique, as long as the updated value always has consistent keys and the binding reference remain.
Here is an example:

How maltiple value using usestate localstorage in react?

I want to work using maltiple values in use state and crud in local storage use state like
const [Data,setData]=[[{
name:'luis',
pass:'1234',
//.......
}]
]
And it updates with the form
<input>
// .......
if value true Display take look at this example I try but I cannot do how to solve it
import './App.css';
import React, { useState,useEffect } from 'react';
function App() {
const [User, setUser] = useState([
{
Name:'',
Pass:'',
Email:'',
}
]);
const [storedUser,setstoredUser]=useState([])
const handle = () => {
localStorage.setItem(JSON.stringfy(...User))
setstoredUser(...User);
};
const remove = () => {
localStorage.removeItem();
};
return (
<div className="App">
<h1>Name of the user:</h1>
<input
placeholder="Name"
name='Name'
value={User.Name}
onChange={(e) => setUser({...User,[e.target.name]:[e.target.value]})}
/>
<h1>Password of the user:</h1>
<input
type="password"
name="Pass"
placeholder="Password"
value={User.Pass}
onChange={(e) => setUser({...User,[e.target.name]:[e.target.value]})}
/>
<h1>Email of the user:</h1>
<input
type="mail"
name="Email"
placeholder="Email"
value={User.Email}
onChange={(e) => setUser({...User,[e.target.name]:[e.target.value]})}
/>
<div>
<button onClick={handle}>Done</button>
</div>
{storedUser.Name && (
<div>
Name: <p>{localStorage.getItem('Name')}</p>
</div>
)}
{storedUser.Pass && (
<div>
Password: <p>{localStorage.getItem('Pass')}</p>
</div>
)}
{storedUser.Email && (
<div>
Password: <p>{localStorage.getItem('Email')}</p>
</div>
)}
<div>
<button onClick={remove}>Remove</button>
</div>
</div>
);
}
export default App;
Here I try how to do this formate I try to all data in state and stringify set this local storage. then remove and display I think explaine on detail
You're missing the key
localStorage.setItem("User",JSON.stringfy(...User))
If you want each key. Loop over they keys and values and set them. As stated by another user, your UserState is an array where it should just be an object
Object.entries(User).forEach(([key,value])=>{
localStorage.setItem(key,value)
})
You are doing a few things incorrectly here:
you are not providing a key for local storage
you don't need to spread objects directly inside setState
use the removeItem method to clear the user from localStorage
you are setting the user as an array to state not an object, this is unnecessary in the example you have
If the goal is to persist a single user between sessions via local storage. Then all you need to do is save the user to local storage when the form is submitted.
Then, when the component loads, check local storage for user data.
import { useEffect, useState } from 'react'
function App() {
const [User, setUser] = useState({
Name: '',
Pass: '',
Email: '',
})
const handle = () => {
const nextUser = JSON.stringify(User)
localStorage.setItem('user', nextUser)
}
const remove = () => {
localStorage.removeItem('user')
}
useEffect(() => {
const storedUser = localStorage.getItem('user')
if (storedUser) {
setUser(JSON.parse(storedUser))
}
}, [])
return (
<div className="App">
<h1>Name of the user:</h1>
<input
placeholder="Name"
name="Name"
value={User.Name}
onChange={(e) => setUser({ ...User, [e.target.name]: e.target.value })}
/>
<h1>Password of the user:</h1>
<input
type="password"
name="Pass"
placeholder="Password"
value={User.Pass}
onChange={(e) => setUser({ ...User, [e.target.name]: e.target.value })}
/>
<h1>Email of the user:</h1>
<input
type="mail"
name="Email"
placeholder="Email"
value={User.Email}
onChange={(e) => setUser({ ...User, [e.target.name]: e.target.value })}
/>
<div>
<button onClick={handle}>Done</button>
</div>
{User.Name && (
<div>
Name: <p>{User.Name}</p>
</div>
)}
{User.Pass && (
<div>
Password: <p>{User.Pass}</p>
</div>
)}
{User.Email && (
<div>
Password: <p>{User.Email}</p>
</div>
)}
<div>
<button onClick={remove}>Remove</button>
</div>
</div>
)
}
export default App
Since User already is an array of user objects I wouldn't spread them in your handle function. You also need to set a key if you're saving in localStorage. This is how to save your list of users under "users":
const handle = () => {
localStorage.setItem("users", JSON.stringfy(User));
setstoredUser(User);
};
Now when retrieving users from the localStorage you can make use of JSON.parse like this:
users = JSON.parse(localStorage.getItem("users") || "[]");
And for deletion you would need the key "users" here as well:
localStorage.removeItem("users");

Error while updating a prefilled input field in React

I prefilled the input fields in my edit-file with data from my database. I can change the values, but when I press my update button, I get the following error:
Warning: A component is changing an uncontrolled input to be
controlled. This is likely caused by the value changing from undefined
to a defined value, which should not happen. Decide between using a
controlled or uncontrolled input element for the lifetime of the
component.
Here is my code:
const savedData = {
img:"",
ressort:"",
theme:"",
title:"",
content:"",
};
const [data, setData] = useState(savedData);
const dispatch = useDispatch();
const {id} = useParams();
useEffect(()=>{
if(isError){
window.alert(message);
}
dispatch(getMainNews(id));
return ()=>{
dispatch(reset());
}
},[dispatch, isError, id, message]);
//Bring Data into savedData
useEffect(()=>{
if(mainnews){
setData({...mainnews})
}
return ()=>{
dispatch(reset());
}
},[mainnews, dispatch]);
//updateData
const updateData = (e)=>{
const {value, name} = e.target;
setData({...data, [name]: value})
// setData((prevState)=>({
// ...prevState,
// [e.target.name]: e.target.value,
// }))
}
const {img, ressort, theme, title, content} = data;
const onSubmit = (e)=>{
e.preventDefault();
const mainnewsData = {
img,
ressort,
theme,
title,
content,
}
dispatch(updateMainNews(mainnewsData))
}
In the return:
<MainNewsForm encType="multipart/form-data" onSubmit={onSubmit}>
<Formgroup>
<Label htmlFor="img">Image</Label>
<Input type="text" name="img" id="img" value={img} onChange={updateData}/>
</Formgroup>
<Formgroup>
<Label htmlFor="ressort">Ressort</Label>
<Input type="text" name="ressort" id="ressort" value={ressort} onChange={updateData}/>
</Formgroup>
<Formgroup>
<Label htmlFor="theme">Theme</Label>
<Input type="text" name="theme" id="theme" value={theme} onChange={updateData}/>
</Formgroup>
<Formgroup>
<Label htmlFor="title">Title</Label>
<Input type="text" name="title" id="title" value={title} onChange={updateData}/>
</Formgroup>
<Formgroup>
<Label htmlFor="content">Content</Label>
<Textarea type="text" name="content" id="content" value={content} onChange={updateData}></Textarea>
</Formgroup>
<ButtonHolder>
<UpdateButton type="submit">Update</UpdateButton>
</ButtonHolder>
</MainNewsForm>

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.

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