The below code is giving me a 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.
when I am changing either password or username.
I am not able to figure out why I am getting this error.
Can someone tell me why exactly i am getting this error....
import React, { useState } from "react";
const Login = () => {
const [login, setLogin] = useState({ user: "", password: "" });
const handleChange = (e) => {
setLogin({ [e.target.name]: e.target.value });
};
return (
<div>
User Name{" "}
<input
onChange={handleChange}
type="text"
name="username"
value={login.user}
/>
Password{" "}
<input
onChange={handleChange}
type="password"
name="password"
value={login.password}
/>
<button>Login</button>
</div>
);
};
Unlike the class component's setState(), setting the state in functional components with useState() doesn't merge the update object with the previous state. Setting the state with useState() replace the previous one. That is why the state in useState() can be arrays, objects, or primitives.
In your case, when you the state - setLogin({ [e.target.name]: e.target.value });, you are replacing the entire state, and in effect remove the value of the other field. You should spread the previous value of the login state, and override just the value of the field that you are changing.
const { useState } = React;
const Login = () => {
const [login, setLogin] = useState({ user: "", password: "" });
const handleChange = (e) => {
setLogin(login => ({ ...login, [e.target.name]: e.target.value }));
};
return (
<div>
User Name{" "}
<input
onChange={handleChange}
type="text"
name="user" // this should be user and not username
value={login.user}
/>
Password{" "}
<input
onChange={handleChange}
type="password"
name="password"
value={login.password}
/>
<button>Login</button>
</div>
);
};
ReactDOM.render(
<Login />,
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>
Related
In the edit page, by redux useSelector method data available to the component but it is not setting initial values. How to set it?
How to set the initial formData from the store?
In hook component rendering happens 4 times why?
import React,{useState} from 'react'
import { useSelector } from 'react-redux'
import {updateUserInfo} from '../actions/User'
const EditUser = (props) => {
const user = useSelector(state => state.user)
const [formData, setFormData] = useState({email: user.email ,fullName: user.fullName})
console.log(user)
const handleSubmit = e =>{
e.preventDefault();
const id = props.match.params.id
const data = new FormData()
data.append('email', formData.email)
data.append('fullName', formData.fullName)
data.append('image', formData.image)
props.dispatch(updateUserInfo(id,data,props.history))
// console.log(formData)
}
const handleChange = e =>{
setFormData({...formData, [e.target.name]: e.target.value })
}
const fileHandle = (e) =>{
// console.log(e.target.files)
setFormData({...formData,image: e.target.files[0]})
}
return (
<React.Fragment>
<h2>Edit Account</h2>
{Object.keys(user).length > 0 &&
<>
<form onSubmit={handleSubmit}>
<label htmlFor="fullName">Full Name</label>
<input type="text" name="fullName" value={formData.fullName} onChange={handleChange}/>
<br />
<label htmlFor="email">Email</label>
<input type="email" name="email" value={formData.email} onChange={handleChange}/>
<br />
<label htmlFor="image">Upload Image</label>
<input type="file" name="image" onChange={fileHandle}/>
<br />
<button >Update</button>
</form>
</>
}
</React.Fragment>
)
}
export default EditUser
useEffect(() => {
setFormData({email:props.user.email || "", fullName: props.user.fullName || ""}) // update the state if data else it is empty string, if not mentioned empty string you will warning uncontrolled component to controlled component vice-versa
},[props.user])
useEffect Hook as componentDidMount, componentDidUpdate, and componentWillUnmount combined.
When store value available to the component, props.user has properties and values then componentDidUpdate method works.
unnecessary rendering also prevented with useEffect method.
Once the checkbox is checked, then I would like to display the input text field. By default the checkbox should not be checked and the token input field should be hidden.
Now I am getting below error and not working properly.
Warning: Failed prop type: You provided a checked prop to a form field without an onChange handler. This will render a read-only field. If the field should be mutable use defaultChecked. Otherwise, set either onChange or readOnly
const [checked, setChecked] = useState(false);
const [showToken, setShowToken] = useState({ show: false });
const handleClick = (e) => {
e.preventDefault();
setChecked(checked);
if(checked === true){
setShowToken({ show: true })
}
}
<label>
<input type="checkbox" onClick={handleClick} checked={checked}/>
{
showToken.show && (
<input className="inputRequest formContentElement" name="token" type="text" placeholder="token"/>
)
}
</label>
We can optimize code as,
Remove const [showToken, setShowToken] = useState({ show: false }); line
Use checked prop for display textbox.
Use setChecked hook for set checkbox checked changes.So we do not need handleClick method anymore.
You can do this simply as follows,
// Get a hook function
const {useState} = React;
const Example = () => {
const [checked, setChecked] = useState(false);
return (
<div>
<label>
<input type="checkbox" onChange={() => setChecked(!checked)} checked={checked}/>
{
checked ? (
<input className="inputRequest formContentElement" name="token" type="text" placeholder="token"/>
) : (<div></div>)
}
</label>
</div>
);
};
// Render it
ReactDOM.render(
<Example />,
document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="react"></div>
I have a form in my react project, and I want the value of each field to be stored in the state.
instead of having multiple states for each field, how can I store the form value as an object in the state? and more importantly how can I access it? (with react hooks)
import React from 'react';
export const UserData = () => {
return(
<form>
<input type="text" placeholder="Name" />
<input type="email" placeholder="Email" />
<button>Confirm</button>
</form>
)
}
React Hooks allows you to define a JavaScript Object using useState. See
https://daveceddia.com/usestate-hook-examples/
function LoginForm() {
const [form, setState] = useState({
username: '',
password: ''
});
Update the form using the function :
const updateField = e => {
setState({
...form,
[e.target.name]: e.target.value
});
};
Call the function onSubmit of the button
<form onSubmit={updateField}>
<input type="text" placeholder="Name" />
<input type="email" placeholder="Email" />
<Button >Confirm</button>
</form>
You can use useState hook. Check this
const [state, setState] = useState({ name, email });
To set the state similar to setState in class based component:
setState({name: 'react', email: 'react'})
To access the state value:
import React, { useState } from 'react';
export const UserData = () => {
const [state, setState] = useState({ name, email });
return(
<form>
<input type="text" placeholder="Name" value={state.name} />
<input type="email" placeholder="Email" value={state.email} />
<button>Confirm</button>
</form>
)
}
You should create an initial state value if you want to store the form values as an object using useState, so you can rollback to the initial state after an error. Example,
const initialState = {
name: "",
email: ""
};
export const UserData = () => {
const [formState, setFormState] = useState(initialState);
const submitHandler = event => {
event.preventDefault();
console.log(formState);
};
return (
<form onSubmit={submitHandler}>
<input
type="text"
placeholder="Name"
value={formState.name}
onChange={e => {
setFormState({ ...formState, name: e.target.value });
}}
/>
<input
type="email"
placeholder="Email"
value={formState.email}
onChange={e => {
setFormState({ ...formState, email: e.target.value });
}}
/>
<button>Confirm</button>
</form>
);
};
Working demo in codesandbox.
I'm new in React js. I created an app that have input fields and when you click the submit button the value of the input fields will display in console.log depends on the value entered in input fields. The question is can i use only one state to display the following that I entered in the input fields?
this is my code:
constructor () {
super ();
this.state = {
username: '',
password: '',
place: '',
birthPlace: '',
}
}
username = () => {
this.setState({ username: event.target.value })
}
password = () => {
this.setState({ password: event.target.value })
}
place = () => {
this.setState({ place: event.target.value })
}
birthPlace = () => {
this.setState({ birthPlace: event.target.value })
}
What I want to happen is use only one state for username, password, place, birthPlace (if possible) and what I want to happen is when I click the submit button, the value of which I've type in the Input fields should display in console.log
Just for optimization purposes. Thanks for the help
You can use the dictionary objects.
Define your state like this
constructor () {
super ();
this.state = {
formvalues:{}
}
}
and you can update your values by defining any key you want.
this.setState(prevState => ({
formvalues: {
...prevState.formvalues,
[username]: value
}
}))
and you can access any input field based on the key you defines.
`console.log(this.state.formvalues.username)`
constructor () {
super ();
this.state = {
username: '',
password: '',
place: '',
birthPlace: '',
}
}
You can use one method for all values like while using the same state:
handleChange = event => {
const { value, name } = event.target;
this.setState({ [name]: value });
};
Its probably better to use hooks for this, something like the code below would do it
import React , {useState} from 'react'
const myComp = () =>{
const [userDetails, setUserDetails] = useState({})
handleOnChange({target:{value, id}}) =>{
let newState = Object.assign({}, userDetails, {[id]: value})
setUserDetails(newState)
}
return (
<form>
<input type='text' id='name' value={userDetails['name']} onChange={handleOnChange} />
<input type='password' id='password' value={userDetails['password']} onChange={handleOnChange} />
<input type='email' id='email' value={userDetails['email']} onChange={handleOnChange} />
</form>
)
}
You can try this example that uses React hooks:
function App() {
const [state, setState] = React.useState({
username: '',
password: '',
place: '',
birthPlace: '',
});
function handleChange(event) {
const { name, value } = event.target;
console.log(name);
console.log(value);
setState(prevState => ({
...prevState,
[name]: value
}));
}
function handleSubmit(event) {
console.log(state);
event.preventDefault();
}
return (
<form onSubmit={handleSubmit}>
<label>UserName</label>
<input type="text" name="username" value={state.username} onChange={handleChange} /><br />
<label>Password</label>
<input type="password" name="password" value={state.password} onChange={handleChange} /><br />
<label>Place</label>
<input type="text" name="place" value={state.place} onChange={handleChange} /><br />
<label>BirthPlace</label>
<input type="text" name="birthPlace" value={state.birthPlace} onChange={handleChange} /><br />
<input type="submit" value="Submit" />
</form>
);
}
ReactDOM.render(<App />, document.getElementById('root'));
.as-console-wrapper {
height: 80px !important;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.2/umd/react-dom.production.min.js"></script>
<div id="root"></div>
More info: https://reactjs.org/docs/forms.html
Be aware that setting the state directly with the event.target.value will not always produce the expecting result.
React uses the Synthetic event wrapper and to be able to update the state directly with the event.target.value it uses persist() to make the event persist long enough to be used in the setState() method.
What I suggest doing to use event.target.value in class components is to store the value and then update the state.
So instead of doing this:
username = (event) => {
this.setState({ username: event.target.value })
}
You do this:
username = (event) => {
const value = event.target.value;
this.setState({ username: value })
}
This way the value is preserved long after the event has been fired.
You will notice that I added the event as parameter in the above example. This is to answer your second part of the question regarding on how to update the state. You need to constantly call the username method each time the typed username is changed. You can do this by calling it via the onChange listener:
<input name='username' onChange = {(event) => this.username(event)} />
Is there another way of getting/setting the values from the dom that is less expensive than useRef()? Is useRef() to be used lightly as the docs suggest?
import React, { useRef, useEffect } from 'react';
const Join: React.FC = () => {
const fullName = useRef<HTMLInputElement>(null);
const email = useRef<HTMLInputElement>(null);
const password = useRef<HTMLInputElement>(null);
const myForm = useRef<HTMLFormElement>(null);
useEffect(() => {
if (myForm.current) myForm.current.reset();
if (fullName.current) fullName.current.focus();
}, []);
return (
<div>
<form ref={myForm}>
<input type='text' ref={fullName} />
<input type='text' ref={email} />
<input type='text' ref={password} />
</form>
</div>
)
}
When the component loads I want to clear the form and focus the
fullName input
You don't need refs for that
I want to clear the form
Make your inputs controlled
Declare an empty string as initial value
const Component = () =>{
const [state, setState] = useState({
email : '',
password : ''
})
const onChange = ({ target: { value, name } }) =>{
setState(prev => ({
...prev,
[name] : value
}))
}
const { email, password } = state
return(
<>
<input value={email} onChange={onChange} id='email'/>
<input value={password} onChange={onChange} id='password' />
</>
)
}
Automatically focus a given input
Just use autofocus for that
<input autofocus/>