I am a newbie in ReactJS and I am making a simple contact manager.
In my InputContact component I take name and email of contact and after submission I store in state variables and pass to parent.
To check my state var is updated , i check my console.
The problem is that, after I submit the form after giving data, I only see a blank line in console. After again clicking on submit, then I see my input in console.
My question is
Why I have to click submit twice , in order to see my state variable getting updated ??
My InputContact.js file
import React from 'react'
import { useState } from 'react';
const InputContact = (props)=>{
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const validateInput = (e)=>{
e.preventDefault();
setName(e.target.fname.value);
setEmail(e.target.femail.value);
console.log(name)
props.addContact(name,email);
}
return(
<>
<form onSubmit={validateInput}>
<label>Name
<input type="text" name='fname' ></input>
</label>
<br/>
<label>Email
<input type="text" name='femail' ></input>
</label>
<button type="submit">Save</button>
</form>
</>
)
};
export default InputContact;
My App.js file is
import Header from './components/Header/Header'
import InputContact from './components/InputContact/InputContact';
import { v4 as uuidv4 } from 'uuid';
function App(){
const [contacts, setContacts] = useState([]);
const addContactFn= (name,email)=>{
setContacts([...contacts, {id:uuidv4(), name:name, email:email}]);
}
return(
<>
<Header />
<InputContact addContact = {addContactFn}/>
</>
)
}
export default App; ```
Your setName call is asynchronous. You cannot guarantee that
console.log(name)
right after
setName(e.target.fname.value);
How you are using state isn't really the normal way. You want to use onChange handlers on the inputs to set the state for each name and email, e.g.
onChange={(e)=> setEmail(e.target.value)}
Then onSubmit of your form should refer to the state variables for name and email, not e.target.value
const validateInput = (e)=>{
e.preventDefault();
props.addContact(name,email);
}
To check the updated value, you can use useEffect hook as
import React from 'react'
import { useState, useEffect } from 'react';
.
.
.
useEffect(() => {
console.log('Name', name);
}, [name]);
const validateInput = (e)=>{
e.preventDefault();
setName(e.target.fname.value);
setEmail(e.target.femail.value);
props.addContact(name,email);
}
.
.
.
I do it like this, so the state updates on typing and when you send it, is already updated.
<form onSubmit={validateInput}>
<label>Name
<input type="text" name='fname' onChange={e => setName(e.target.value) ></input>
</label>
<br/>
<label>Email
<input type="text" name='femail' onChange={e => setEmail(e.target.value) ></input>
</label>
<button type="submit">Save</button>
</form>
Related
I am getting this error while updating the isSeller option it is working for admin but not for seller.
Warning: A component is changing an uncontrolled input of type text to be controlled. Input elements should not switch from uncontrolled to controlled (or vice versa). Decide between using a controlled or uncontrolled input element for the lifetime of the component.*
Following is my code:
import React from 'react';
import { useEffect } from 'react';
import { useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { detailsUser } from '../actions/userActions';
import LoadingBox from '../components/LoadingBox';
import MessageBox from '../components/MessageBox';
export default function UserEditScreen(props) {
const userId = props.match.params.id;
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const [isSeller, setIsSeller] = useState(false);
const [isAdmin, setIsAdmin] = useState(false);
const userDetails = useSelector((state) => state.userDetails);
const { loading, error, user } = userDetails;
const dispatch = useDispatch();
useEffect(()=>{
if(!user){
dispatch(detailsUser(userId));
}
else{
setName(user.name);
setEmail(user.email);
setIsSeller(user.isSeller);
setIsAdmin(user.isAdmin);
}
},[dispatch, user, userId])
const submitHandler = (e) => {
e.preventDefault();
};
return (
<div>
<form className="form" onSubmit={submitHandler}>
<div>
<h1>Edit User {name}</h1>
{loading && <LoadingBox></LoadingBox>}
{error && (
<MessageBox variant="danger">{error}</MessageBox>
)}
</div>
{loading ? (
<LoadingBox />
) : error ? (
<MessageBox variant="danger">{error}</MessageBox>
) : (
<>
<div>
<label htmlFor="name">Name</label>
<input
id="name"
type="text"
placeholder="Enter name"
value={name}
onChange={(e) => setName(e.target.value)}
></input>
</div>
<div>
<label htmlFor="email">Email</label>
<input
id="email"
type="email"
placeholder="Enter email"
value={email}
onChange={(e) => setEmail(e.target.value)}
></input>
</div>
<div>
<label htmlFor="isSeller">Is Seller</label>
<input
id="isSeller"
type="checkbox"
checked={isSeller}
onChange={(e) => setIsSeller(e.target.checked)}
></input>
</div>
<div>
<label htmlFor="isAdmin">Is Admin</label>
<input
id="isAdmin"
type="checkbox"
checked={isAdmin}
onChange={(e) => setIsAdmin(e.target.checked)}
></input>
</div>
<div>
<button type="submit" className="primary">
Update
</button>
</div>
</>
)}
</form>
</div>
);
}
This warning comes when your value changes from an undefined to defined value or vice-versa.
For. e.g.
import React, {useState} from 'react'
const MyComponent = () => {
const [email, setEmail] = useState() // Look, the value of email will be undefined in the begining
return (
<input type="text" value={email}
{/* You can put value={email || ''} or value={isSeller || false} to fix this, so that the value passed to input is always defined or otherwise you need to make sure that the state value is never undefined */}
onChange={(e) => setEmail(e.target.value)}
/>
)
}
export default MyComponent;
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
In your case, your input values depend on name, email, isSeller, and isAdmin. Ofcourse those values are defined in the beginning, but you have a useEffect, where you are updating the value to user.name etc. I believe in this useEffect, your states value gets undefined, because your user is undefined initially.
PS. This is only a warning, you can ignore it. Or just make sure that the value passed to input field is always defined or that it doesn't transition from undefined -> defined (i.e. uncontrolled -> controlled)
This could be most probably because you might be getting undefined in your response of user.isSeller as explained here https://stackoverflow.com/a/47012342/10212656.
Or you can simply add !! in checked value obj of checkbox to solve this.
hi guys i am trying to send the data from NewComponent component to App component in react while i am trying to do this it gives me an error and i tried hrad to solve this problem but i coudn't
here as you see this is the component that i will take the information form it and i will send it to newcomponent component exactly i will send expenseData object's information up (from child to parent ) but when i do this it gives me this error (props.onAddNewExpense is not a function)
this is the expense form component
import React , {useState} from "react";
import "./ExpenseForm.css"
const ExpenseForm = (props) => {
//the setEnteredTitle will be like a function i can do a refreshing with it .
const [enteredTitle,setEnteredTitle]=useState("")
const [enteredAmount,setEnteredAmount]=useState("")
const [enteredDate,setEnteredDate]=useState("")
const TitleChangeHandeler=(eo) => {
setEnteredTitle(eo.target.value)
console.log(setEnteredTitle)
}
const amountChangeHnadeler= (eo) => {
setEnteredAmount(eo.target.value)
}
const DateChangeHnadeler= (eo) => {
setEnteredDate(eo.target.value)
}
const submitHandeler=(eo) => {
eo.preventDefault(); // it make me to prevent the dafoult that make the page refresh
const expenseData={
Title:enteredTitle,
Amount:enteredAmount,
Date:new Date(enteredDate)
}
console.log(expenseData)
props.onSaveExpenseData(expenseData)
//when the user make a submit it will give a empty data for the sates
setEnteredTitle("");
setEnteredAmount("");
setEnteredDate("");
}
return (
<form onSubmit={submitHandeler}>
<div className="new-expense__controls">
<div className="new-expense__control">
<label>Title</label>
<input type="text" className="input" onChange={TitleChangeHandeler} value={enteredTitle} />
</div>
<div className="new-expense__control">
<label> Amount </label>
<input type="number" step="0.01" min="0.01" className="input" onChange={amountChangeHnadeler} value={enteredAmount}/>
</div>
<div className="new-expense__control">
<label>DATE</label>
<input type="date" min="3-12-2021" max="01-01-2025" onChange={DateChangeHnadeler} value={enteredDate} />
</div>
<div className="new-expense__actions">
<button type="submit"> Add expense </button>
</div>
</div>
</form>
);
};
export default ExpenseForm;
and this is the parent component
import React from 'react'
import "./NewComponent.css"
import ExpenseForm from "./ExpenseForm"
const NewComponent = (props) => {
const saveExpenseDataHandeler= (enteredExpenseData) => {
const expenseData = {
...enteredExpenseData ,
id:Math.random().toString()
}
props.onAddNewExpense(expenseData)
}
//here we just point to the function we did not execute it here
return (
<div className="new-expense">
<ExpenseForm onSaveExpenseData={saveExpenseDataHandeler} />
</div>
)
}
export default NewComponent
we can see that i fetch the component props and i am trying to take the child data from there
thanks alot for any answer will come
So basically my problem is that I have two pages/screens (Main & Edit). In Main page when I click an item then it passes it to Edit page using useHistory hooks from react-router-dom package. In Edit page I get the item using useLocation hooks & pass them to input field for my initial useState, but every time I edit/type a character in text field not sure if its called re render or anything else, but the useLocation will be passed in console. Here is my code:
Edit Page/Screen
import axios from "axios";
import React, { useState } from "react";
import { useLocation } from "react-router";
export const EditScreen = () => {
const location = useLocation().state;
console.log(location);
const [title, setTitle] = useState(location.b_title);
const [content, setContent] = useState(location.b_content);
const [category, setCategory] = useState(location.category_id);
const submitHandler = (e) => {
e.preventDefault();
axios
.put(`http://localhost:3001/api/v1/blog/${location.id}`, {
blog_title: title,
blog_content: content,
category_id: category,
})
.then(alert("success edit blog"))
.catch((err) => alert(err));
setTitle("");
setContent("");
setCategory("");
};
return (
<div>
<h1>edit blog page</h1>
<form onSubmit={submitHandler}>
<input
type="text"
placeholder="title"
value={title}
onChange={(e) => setTitle(e.target.value)}
/>
<br />
<br />
<input
type="text"
placeholder="content"
value={content}
onChange={(e) => setContent(e.target.value)}
/>
<br />
<br />
<input
type="text"
placeholder="category"
value={category}
onChange={(e) => setCategory(e.target.value)}
/>
<br />
<br />
<input
type="submit"
value="submit"
disabled={
title === "" || content === "" || category === "" ? true : false
}
/>
</form>
</div>
);
};
Screen Shot of Console
You can see above ss in red circle, thats the problem.
Bro, We cant prevent the setState from re-rendering the component as it's the react specified default before. So you must use the debouncing technique to prevent the multiple re-renders from happening https://www.telerik.com/blogs/debouncing-and-throttling-in-javascript u can go through this article for better clarity of how you can do that. It's mentioned with examples clearly in the article. You can also change the event to onBlur instead of onChange we reduce the number of setStates from getting called & we reduces the re-rendering.
I would like to know why loginPassword.length and loginPasswordError is different inside and outside of loginFormPasswordHandler
import React, {useState} from 'react';
import './styles.css'
const App = () => {
const [loginPassword, setLoginPassword] = useState('');
const [loginPasswordError, setLoginPasswordError] = useState();
const [submitController, setSubmitController] = useState(false);
const loginFormSubmitHandler = (e) => {
e.preventDefault();
}
const loginFormPasswordHandler = (e) => {
setLoginPassword(e.target.value);
setLoginPasswordError(loginPassword.length < 8);
console.log('login password length is(inside):'+loginPassword.length+' and the state is '+loginPasswordError)
loginPassword.length > 8 ? setSubmitController(true) : setSubmitController(false);
}
console.log('login password length is(outside):'+loginPassword.length+' and the state is '+loginPasswordError)
return(
<React.Fragment>
<div className="form-wrapper">
<form onSubmit={loginFormSubmitHandler}>
<input className={`${loginPasswordError && 'error'}`} type="password" id="password" name="password" placeholder="Password" onChange={loginFormPasswordHandler} />
<div className={`submit-btn ${submitController ? '' : 'disable'}`}>
<input type="submit" />
</div>
</form>
</div>
</React.Fragment>
);
}
export default App;
I know useState re-runs the entire code when the state is changed. But I can't understand this behavior. I am not sure whether this is a Javascript property or React property.
setState is asynchronous, meaning your login password and error state values might not update immediately after you run setLoginPassword and setLoginPasswordError.
The other line below re-runs on every render, so it will output up to date values.
console.log('login password length is(outside):'+loginPassword.length+' and the state is '+loginPasswordError)
I have a react component which manage user logging in and out, when user type email and password in the login field the whole component (Navbar) re-render to Dom in every keystroke unnecessarily thus reduces speed.
How can I prevent Navbar from re-rendering when user type their credential in login fild ?
import React, { useContext,useState } from 'react';
import { Postcontext } from '../contexts/Postcontext';
import axios from 'axios';
const Navbar = () => {
const { token,setToken } = useContext(Postcontext);
const [email,setEmail] = useState(''); **state manages user email for login**
const [password,setPassword] = useState(''); **state manages user password for login**
const[log,setLog] = useState(true) **state manages if user logged in or not based on axios post request**
const login=(e)=>{
//function for login using axios
})
}
const logout=(e)=>{
//function for logout using axios
}
return (
<div className="navbar">
{log?(
<form>
<input value={email} type="text" placeholder="email" onChange={(e)=>setEmail(e.target.value)}/>
<input value={password} type="text" placeholder="password" onChange={(e)=>setPassword(e.target.value)}/>
<button onClick={login}>login</button>
</form>
):(
<button onClick={logout}>logout</button>
)}
</div>
);
}
export default Navbar;
It is because it is same component which needs re-render to reflect input text changes. If you want your email to change but not effect Navbar then create a child component and move inputs into that component, manage input values using useState() there in child component and when you finally submit and user is logged in then you can either update some global state like redux store or global auth context to reflect and rerender Navbar.
So, I had the same issue and I was able to solve it using useRef and useCallback and I will try to explain in Q&A form. Sorry if I am not that clear, this is my first StackOverFlow comment and I am a beginner in React :)
Why useRef?
React re-renders every time it sees a component has updated by checking if previous and current object are same or not. In case of useRef it checks the object Id only and not the content inside it i.e. value of current inside the Ref component. So if you change the value of current React will not consider that. (and that's what we want)
Why useCallback?
Simply because it will run only when we call it or one (or more) of the dependencies have changed. As we are using Ref so it won't be called when the current value inside it has changed.
More info: https://reactjs.org/docs/hooks-reference.html
Based on above info your code should look like this (only doing login part):
import React, { useContext, useRef } from 'react';
const App = () => {
const emailRef = useRef(null);
const passwordRef = useRef(null);
const logRef = useRef(null);
const loginUpdate = useCallback( async (event) => {
event.preventDefault();
// Your logic/code
// For value do:
// const email = emailRef.current.value;
}, [emailRef, passwordRef, logRef]);
return (
<div className="navbar">
{log?(
<form>
<input
ref={emailRef}
type="text"
placeholder="email"
/>
<input
ref={passwordRef}
type="text"
placeholder="password"
/>
<button onClick={loginUpdate}>login</button>
</form>
):(
// Not doing this part because I am lazy :)
<button onClick={logout}>logout</button>
)}
</div>
);
}
export default App;
Had a few typos. It works for me
https://codesandbox.io/s/cold-sun-s1225?file=/src/App.js:163-208
import React, { useContext,useState } from 'react';
// import { Postcontext } from '../contexts/Postcontext';
// import axios from 'axios';
const App = () => {
// const { token,setToken } = useContext();
const [email,setEmail] = useState('');
const [password,setPassword] = useState('');
const[log,setLog] = useState(true)
const login=(e)=>{
//function for login using axios
}
const logout=(e)=>{
//function for logout using axios
}
return (
<div className="navbar">
{log?(
<form>
<input value={email} type="text" placeholder="email" onChange={(e)=>setEmail(e.target.value)}/>
<input value={password} type="text" placeholder="password" onChange={(e)=>setPassword(e.target.value)}/>
<button onClick={login}>login</button>
</form>
):(
<button onClick={logout}>logout</button>
)}
</div>
);
}
export default App;