I have two input forms email and password with onBlur to validate them and a Button for onSubmit. My question is, if email form onBlur executed when I start typing on password form, how about that password form when I'll click directly next the onSubmit button, isn't? That means (because Javascript and setState() are asynchronous) the onBlur may not be executed first or at least onSubmit may start its logic execution before finishing the onBlur logic of Password form, so a state in onSubmit may have wrong data cause it didn't wait password form onBlur? (I really hate that fact in web development)
When I look at console.log I see it respected password onBlur, setState() execution and waited for its callback, but I'm not sure if it will work the same every time. (console.log(Blur) line is only in password onBlur not in email for testing)
Edit : I tested again and it appears that sometimes if password form has an error the submit Button wont execute any line in onSubmit code, so I need sometimes after correcting password error to click twice on Submit button to make onSubmit code executed. How to deal with that conflict?
Code:
Form:
const SignInForm = (props) => {
let enabledOrDisabled = props.signInFormCanEvent ? false : 'disabled';
return (
<React.Fragment>
<h1>Welcome to ToDo</h1>
<form onSubmit={props.signInSubmitHandler} className={style.signInForm}>
<span className={style.userFormsErrors}>{props.userNotFoundError}</span>
<div className={style.signInFormImportantElements}>
<span className={style.userFormsErrors}>{props.userEmailError}</span>
<input
name="userEmail"
type="email"
placeholder="email"
value={props.currentUserEmailText}
className={style.signInText}
onChange={(e) => {
props.signInOnChangeHandler(e);
}}
onBlur={(e) => props.signInOnBlurHandler(e)}
disabled={enabledOrDisabled}
/>
<span className={style.userFormsErrors}>{props.userPasswordError}</span>
<input
name="userPassword"
type="password"
placeholder="password"
value={props.currentUserPasswordText}
className={style.signInText}
onChange={(e) => {
props.signInOnChangeHandler(e);
}}
onBlur={(e) => props.signInOnBlurHandler(e)}
disabled={enabledOrDisabled}
/>
<label>
Remember me
<input
type="checkbox"
name="rememberMe"
onChange={(e) => props.signInOnChangeHandler(e)}
disabled={enabledOrDisabled}
/>
</label>
<input type="submit" value="Submit" className={style.signInSubmit} disabled={enabledOrDisabled} />
</div>
<div className={style.signInLinks}>
Forget Password
{/*Create Account*/}
<input type="button" value="SignUp" onClick={props.signUpLinkHandler} />
</div>
</form>
</React.Fragment>
);
};
onBlur :
signInOnBlurHandler(e) {
e.preventDefault();
const { name, value } = e.target;
let tmpErrors = { ...this.state.signInErrors };
switch (name) {
case 'userEmail':
tmpErrors.userEmailError = validEmailRegex.test(value) ? '' : 'Email is not valid!';
this.setState({ signInErrors: tmpErrors });
break;
case 'userPassword':
tmpErrors.userPasswordError = value.length >= 8 ? '' : 'Password must be at least 8 characters long!';
this.setState({ signInErrors: tmpErrors }, () => {
console.log('blur');
});
break;
}
}
onSubmit :
signInSubmitHandler(e) {
e.preventDefault();
console.log('submit');
let signInErrorsTmp = { ...this.state.signInErrors };
signInErrorsTmp.userNotFoundError = '';
this.setState({ signInErrors: signInErrorsTmp }, () => {
let canSubmit = true;
for (let element in this.state.signInErrors) {
if (this.state.signInErrors[element].length > 0) {
canSubmit = false;
}
}
if (canSubmit) {
const config = {
//crossDomain: true,
withCredentials: true
};
axios
.post(
'http://localhost/React_ToDo_APP/to-do/src/PHPFiles/UserSignIn.php',
this.state.signInUserInfo,
config
)
.then((response) => {
if (response.status == 201) {
const tmpSignInUserInfo = { ...this.state.signInUserInfo };
tmpSignInUserInfo.currentUserEmailText = '';
tmpSignInUserInfo.currentUserPasswordText = '';
tmpSignInUserInfo.currentUserRememberMe = false;
this.setState({ signInUserInfo: tmpSignInUserInfo }, () => {
const signedInUserSessionDataTmp = { ...this.state.signedInUserSessionData };
signedInUserSessionDataTmp.userName = response.data.userName;
this.setState({ signedInUserSessionData: signedInUserSessionDataTmp }, () => {
this.setState({ isUserSignedIn: true }, () => {
this.resetAndGetData();
});
});
});
}
})
.catch((error) => {
console.log('Error: ', error);
if (error.response.status == 401) {
let signInErrorsTmp = { ...this.state.signInErrors };
if (error.response.data.emailError.length > 0)
signInErrorsTmp.userEmailError = error.response.data.emailError;
if (error.response.data.passwordError.length > 0)
signInErrorsTmp.userPasswordError = error.response.data.passwordError;
if (
error.response.data.hasOwnProperty('userNotFoundError') &&
error.response.data.userNotFoundError.length > 0
)
signInErrorsTmp.userNotFoundError = error.response.data.userNotFoundError;
this.setState({ signInErrors: signInErrorsTmp });
}
});
}
});
Based on the below, so long as the form is submitted by a click, and not enter keydown, then onBlur handler should apply first, and onSubmit handler will receive correct state.
class Test extends React.Component {
state={
value1:"",
value2:"",
validated1: '',
validated2: ''
}
handleSubmit = (e) => {
e.preventDefault();
console.log("submit", this.state);
}
handleBlur = (e) => {
const target = e.target;
this.setState({["validated" + target.id]: target.value.toUpperCase()})
console.log("blur");
}
handleChange = (e) => {
const target = e.target;
this.setState({["value" + target.id]:target.value});
}
render() {
return(
<form onSubmit={this.handleSubmit}>
<input type="text" onChange={this.handleChange} onBlur={this.handleBlur} value={this.state.value1} id="1"/>
<input type="text" onChange={this.handleChange} onBlur={this.handleBlur} value={this.state.value2} id="2"/>
<input type="submit" value="submit" />
</form>
)
}
}
ReactDOM.render(<Test/>, document.getElementById("root"));
<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>
<div id="root"></div>
Related
As the title states, Ive got a simple crud operation I could use some help with the useEffect/useState hook in how to implement.
I've got the input set to disabled by default, displaying the previous value via the placeholder. The state is then updated via a useState/useEffect hooks. Also, the second button (save) is set to hidden by default.
Goal:
essentially, just trying to setup an event listener for each edit-input button: hide the edit button, enable the input, unhide the save button.
with a separate (2nd) event listener on the save button: to hide the save button, unhide the edit button, return the input to disabled, turn the placeholder to the new value, and submit the value(I've got a good idea of the last part)
JSX:
<label className="col">
<div className="row">
<p className="bold nowrap inline-value-title" >Test Name: </p>
<input
id="display-name-change"
type="text"
className="form-reset inline-input"
onChange={(e) => setDisplayName(e.target.value)}
value={displayName}
placeholder={user.displayName}
disabled
/>
<button type="button" className="inline-button edit-val-btn">
<FontAwesomeIcon icon={faPenToSquare} />
</button>
<button type="button" className="hidden inline-button save-val-btn">.
<FontAwesomeIcon icon={faCircleCheck} />
</button>
</div>
</label>
My Javascript: (as you can probably tell it's still very Vanilla and I think that's the problem)...
const editValueBtns = document.querySelectorAll('.edit-val-btn');
const saveValueBtns = document.querySelectorAll('.save-val-btn');
useEffect(() => {
editValueBtns.forEach((button) => {
button.addEventListener('click', (e) => {
button.classList.add('hidden')
button.nextSibling.classList.remove('hidden')
button.parentElement.children[1].removeAttr("disabled") ;
})
})
saveValueBtns.forEach((button) => {
button.addEventListener('click', (e) => {
button.classList.add('hidden')
button.previousSibling.classList.remove('hidden')
button.parentElement.children[1].addAttr("disabled") ;
})
})
}, []);
EDIT: Showing the inputs being submitted to Firebase/Firestore
const handleSubmit = async (e) => {
e.preventDefault();
let selectedFile = document.querySelector('#thumbnailInput')
// displayname
if(displayName.length == 0){console.log('No change of name')}
else {
console.log('change to displayname')
updateProfile(user, { displayName })
setDoc(doc(db, 'users', user.uid), { displayName }, { merge: true })
}
// phone Number
if (phoneNo.length == 0){console.log('No change of phone no')}
else {
console.log('change to phone')
updateProfile(user, { phoneNo })
setDoc(doc(db, 'users', user.uid), { phoneNo }, { merge: true })
}
// title
if (title.length == 0){console.log('No change of title')}
else {
console.log('change to title')
updateProfile(user, { title })
setDoc(doc(db, 'users', user.uid), { title }, { merge: true })
}
// avatar thumbnail
if(selectedFile.files[0] == undefined){
console.log('no change to thumbnail')
} else {
console.log('change to thumbnail')
// pass the path in ref to create a StorageReference
const storageRef = ref(storage,`thumbnails/${user.uid}/${displayName}`) //thumbnail.name
// upload image, file is a blob here
await uploadBytes(storageRef, thumbnail);
const downloadUrl = await getDownloadURL(storageRef);
// this function returns promise too, add await
await updateProfile(user, { photoURL: downloadUrl })
updateProfile(user, { photoURL: downloadUrl})
setDoc(doc(db, 'users', user.uid), {
photoURL: downloadUrl,
}, { merge: true })
}
// clear all form inputs
const inputs = e.target.querySelectorAll('.form-reset')
inputs.forEach((input) => {
input.value=""
})
}
I see your vanilla js way, and raise you the react way. In react, you shouldn't have to use document.querySelector, previousSibling, parentElement, classList.add, classList.remove, addAttr or button.addEventListener. See solution in CodeSandbox or below:
App.jsx
import { Row } from "./components/Row";
import "./styles.css";
export default function App() {
return (
<div className="App">
<Row placeholder="input 1" />
<Row placeholder="input 2" />
<Row placeholder="input 3" />
</div>
);
}
Row.jsx
import { useState } from "react";
export const Row = ({ defaultValue, placeholder }) => {
const [value, setValue] = useState(defaultValue);
const [disabled, setDisabled] = useState(true);
const handleEditClick = () => {
setDisabled(false);
};
const handleSaveClick = () => {
setDisabled(true);
// save logic goes here
};
return (
<label className="col">
<div className="row">
<p className="bold nowrap inline-value-title">Test Name:</p>
<input
type="text"
className="form-reset inline-input"
onChange={(e) => {
setValue(e.target.value);
}}
value={value}
placeholder={placeholder}
disabled={disabled}
/>
{disabled && (
<button
type="button"
onClick={handleEditClick}
className="inline-button edit-val-btn"
>
edit
</button>
)}
{!disabled && (
<button
type="button"
onClick={handleSaveClick}
className="hidden inline-button save-val-btn"
>
save
</button>
)}
</div>
</label>
);
};
small tweaks.. it appears that logging the console kept it from doing what it did. also removed the e from the parenthesis after each 'click'. the addAttr and removeAttr also were replaced... the rest of the functionality can be placed in either of the listeners..
EDIT: added a 2nd input and it appears to only work in the 1st input....
...yes I'm talking to myself.
EDIT 2: It worked fine until the page refreshed... removed the dependency array for it to work every time. I feel like i still need a cleanup function, but I can't just place the event listeners into a function can i? Really, if you're reading this i would love some more experienced input... :)
useEffect(() => {
editValueBtns.forEach((button) => {
button.addEventListener('click', () => {
button.classList.add('hidden')
button.nextSibling.classList.remove('hidden')
button.parentElement.children[1].disabled = false ;
})
})
saveValueBtns.forEach((button) => {
button.addEventListener('click', () => {
button.classList.add('hidden')
button.previousSibling.classList.remove('hidden')
button.parentElement.children[1].disabled = true ;
})
})
});
I've coded a OTP component but it has two issues that I couldn't solve.
The first issue is, when I press Backspace I want to delete the input value and then go to the previous input field to delete it.
The second issue is, when I delete a input (i.e the 3rd box) my code makes me focus on the next input field.
How can I implement Backspace key press properly and go to the previous input field when a input is deleted?
Here's what I've done so far:
import React, { useState } from "react";
import css from '../components/css/OTP.css'
const Axios = require('axios')
const OTP = () => {
const [otp, setOtp] = useState(new Array(4).fill(""));
const handleChange = (element, index) => {
if (isNaN(element.value)) return false;
setOtp([...otp.map((d, idx) => (idx === index ? element.value : d))]);
//Focus next input
if (element.nextSibling) {
element.nextSibling.focus();
}
}
//eğer girilen OTP backendden gelen OTP ile aynıysa matchleştiğini göster ve kullanıcıyı verifike et daha sonra dashboarda aktar.
const checkOTP = async () =>{
try
{
const url = "http://localhost:5000/auth/otp"
const response = await Axios.post(url , {otp} , {withCredentials: true})
if(response.status!==200){
alert("Invalid OTP entry")
}
else{
alert("OTP successful!")
}
}
catch(err)
{
console.log(err)
}
}
return (
<>
<div className="otp-window">
<div className="otp-modal">
<div className="row">
<div className="info">
<p className="otp-info">Please enter the OTP sent to your email</p>
{otp.map((data, index) => {
return (
<input
className="otp-field"
type="text"
name="otp"
maxLength="1"
key={index}
value={data}
onChange={e => handleChange(e.target, index)}
onFocus={e => e.target.select()}
/>
);
})}
<p className="otp-entered">OTP : {otp.join("")}</p>
<p>
<button
className="clear-button"
onClick={e => setOtp([...otp.map(v => "")])}
>
Clear
</button>
<button
className="verify-button"
onClick={e =>
alert("Entered OTP is " + otp.join(""))
}
onClick={checkOTP}>
Verify OTP
</button>
</p>
</div>
</div>
</div>
</div>
</>
);
};
export default OTP;
You can use an input event and check the event.inputType property of the event object like this. You might have to change the onChange prop to onInput though.
if (event.inputType === 'deleteContentBackward' && event.target.value === '') {
// Focus on the previous field
}
I'm new to React and have written the following form which sends the data to Firebase, this part works but on submit I want to redirect to /thankyou.html which is outside of the react app.
Can anyone advise how I redirect after submit please?
My form is as follows:
import React, { Component } from 'react';
import firebase from '../firebase.js';
class Form extends Component {
constructor(){
super();
this.state = {
name : "",
email : "",
phone : "",
message : "",
formError: false
}
}
getName = (e) =>{
let username = e.target.value;
this.setState({
name: username
});
}
getPhone = (e) =>{
let userphone = e.target.value;
this.setState({
phone: userphone
});
}
getEmail = (e) =>{
let userEmail = e.target.value;
//the most important thing is that we use a RegEx
//in order to manage the input of the email
//at least we can get a some what valid email
if(userEmail.match(/^([a-zA-Z0-9_\-\.]+)#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$/)){
this.setState({
email: userEmail
});
}else{
this.setState({
email: ""
});
}
}
getDescription = (e) =>{
let userMessage = e.target.value;
this.setState({
message: userMessage
});
}
//send the form
submitForm = (e) =>{
e.preventDefault()
const itemsRef = firebase.database().ref('items');
if(this.state.name === "" || this.state.email === "" || this.state.phone === "" || this.state.message === "" ){
this.setState({
formError: true
})
return false;
}else{
this.setState({
formError: false
})
const item = {
Name: this.state.name,
Email: this.state.email,
Phone: this.state.phone,
Message: this.state.message
}
itemsRef.push(item);
this.setState({
name: '',
email: '',
phone: '',
message: ''
})
}
}
render() {
return (
<form onSubmit={this.handleSubmit}>
{/* I am just sending a basic error message */}
{this.state.formError &&
<p className="error">
Fill all the input fields please.
</p>
}
<div>
<label htmlFor="name">Name</label>
<input type="text" name="name" placeholder="Your name here please" onChange={this.getName} />
</div>
<div>
<label htmlFor="email">Email</label>
<input type="email" name="email" placeholder="We will contact you after reviewing your message" onChange={this.getEmail} />
</div>
<div>
<label htmlFor="phone">Phone</label>
<input type="phone" name="phone" placeholder="We will contact you after reviewing your message" onChange={this.getPhone} />
</div>
<div>
<label htmlFor="name">Message</label>
<textarea onChange={this.getDescription} maxLength="450"></textarea>
</div>
<div>
<p>We will answer as soon as possible</p>
<input type="submit" name="submit" value="Send" onClick= {this.submitForm} />
</div>
</form>
);
}
}
export default Form;
in firebase push() can have a callback function as a second parameter which you can use in your case to check whenever the save process to firebase is done redirect to thank you page.
so after submitting the form and save to firebase, you can redirect to another page like that :
itemsRef.push(item, ()=>{
window.location.href = "/thankyou.html"; // similar behavior as clicking on a link
});
Use react-router-dom npm package.
import {withRouter, BrowserRouter } from 'react-router-dom';
Wrap your App.js component with BrowserRouter
<BrowserRouter><App /></BrowserRouter >
Now Wrap your component with withRouter where you have submit handler function.
export default withRouter(yourcomponentname);
submitForm = (e) =>{
this.props.history.push('/url');
}
Try this in your submitForm function after doing all stuff. hope it will help you.
document.location = "/thankyou.html" if the file is in root directory, and document.location = "thankyou.html" if the file is in relative directory.
submitForm = (e) =>{
this.props.history.push('/thankyou');
}
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> );
This question already has answers here:
React - clearing an input value after form submit
(9 answers)
Closed 4 years ago.
I am having some difficulty getting my input form to reset on submit. I would like for the input field to reset to a blank value when the form is submitted successfully, but for the time being I am also fine with it just resetting onSubmit in general, neither of which I have been able to figure out so far. Specifically, what I tried was:
class SubscribeForm extends React.Component {
constructor(props, ...args) {
super(props, ...args)
this.state = {
status: null,
msg: null,
};
this.handleSubmit = this.handleSubmit.bind(this);
}
handleSubmit(event) {
event.preventDefault();
this.setState({ value: '' });
}
With the form layout:
<form onSubmit={this.handleSubmit} action={action} method="post" id="alert-form" noValidate>
<input
ref={node => (this.input = node)}
type="email"
defaultValue=""
name="EMAIL"
required={true}
placeholder={messages.inputPlaceholder}
/>
<button
disabled={this.state.status === "sending" || this.state.status === "success"}
onClick={this.onSubmit}
type="submit"
className="btn-group"
id="AlertButton"
>
<p>Sign Up</p>
</button>
</form>
However, calling {this.handleSumbit} in the onSubmit prop of the Form does not appear to have any result when the button is actually clicked. Is there anything obvious i'm missing here or is it a more complex problem?
Provided below is the full relevant code snippet:
class SubscribeForm extends React.Component {
constructor(props, ...args) {
super(props, ...args)
this.state = {
status: null,
msg: null,
};
this.handleSubmit = this.handleSubmit.bind(this);
}
handleSubmit(event) {
event.preventDefault();
this.setState({ value: '' });
}
onSubmit = e => {
e.preventDefault()
if (!this.input.value || this.input.value.length < 5 || this.input.value.indexOf("#") === -1) {
this.setState({
status: "error"
})
return
}
const url = getAjaxUrl(this.props.action) + `&EMAIL=${encodeURIComponent(this.input.value)}`;
this.setState(
{
status: "sending",
msg: null
}, () => jsonp(url, {
param: "c"
}, (err, data) => {
if (err) {
this.setState({
status: 'error',
msg: err
})
} else if (data.result !== 'success') {
this.setState({
status: 'error',
msg: data.msg,
})
} else {
this.input.defaultValue = "";
this.setState({
status: 'success',
msg: data.msg,
inputPlaceholder: "E-mail"
})
}
})
)
}
render() {
const { action, messages, className, style, styles } = this.props
const { status, msg } = this.state
return (
<div className={className} style={style}>
<form onSubmit={this.handleSubmit} action={action} method="post" id="alert-form" noValidate>
<input
ref={node => (this.input = node)}
type="email"
defaultValue=""
name="EMAIL"
required={true}
placeholder={messages.inputPlaceholder}
/>
<button
disabled={this.state.status === "sending" || this.state.status === "success"}
onClick={this.onSubmit}
type="submit"
className="btn-group"
id="AlertButton"
>
<p>Sign Up</p>
</button>
</form>
</div>
)
}
}
Try this
handleSubmit(event) {
event.preventDefault();
this.input.value = '';
this.setState({ value: '' });
}
https://codepen.io/va0000ll/pen/zRXNwY
You could make SubscribeForm a controlled component. https://reactjs.org/docs/forms.html
This means adding an onChange that updates this.state.value and setting the value of the input field to this.state.value.
If you do this your setState({value: ''}) would cause a render of the form with the value set back to empty string.
It creates a two way binding. When the input field is updated, so is the component state. When the component state is updated, so is the input field.
Since you are using onSubmit, you can get the input field from the event. Something like event.target.elements.EMAIL.value = ''. I would prefer this over using the ref that you have there.