How reset form values using react bootstrap - javascript

My goal after clicking the register button is:
Make input fields blank
Do not show error tooltips
Here is the link on CodeSandbox
I've already tried using event.target.reset(); however the tooltips are still appearing on the screen.
export default function App() {
const [showSucessAlert, setshowSucessAlert] = useState(false);
const [validated, setValidated] = useState(false);
const [transitionAlert, setTransitionAlert] = useState(false);
const handleSubmit = (event) => {
const form = event.currentTarget;
event.preventDefault();
if (form.checkValidity() === false) {
event.stopPropagation();
} else {
handleClickTransitionAlert();
setshowSucessAlert(true);
}
setValidated(true);
};
const handleClickTransitionAlert = () => {
setTransitionAlert(true);
setTimeout(() => {
setTransitionAlert(false);
}, 1700);
};
return (
<Form noValidate validated={validated} onSubmit={handleSubmit}>
<Form.Group className="position-relative" controlId="validationPassword">
<Form.Label>Password</Form.Label>
<InputGroup hasValidation id="validationPassword" />
<Form.Control
type="password"
aria-describedby="validationPassword"
required
/>
<Form.Control.Feedback tooltip type="invalid">
Please enter your Password.
</Form.Control.Feedback>
</Form.Group>
<Alert
className={`mt-1 p-1 position-fixed ${
transitionAlert ? "alert-shown" : "alert-hidden"
}`}
show={showSucessAlert}
variant="success"
>
Registered user!
</Alert>
<Button className="mt-5" variant="primary" type="submit">
Register
</Button>
</Form>
);
}
Here is the link on CodeSandbox
Every help is welcome!

I don't commonly use uncontrolled components, but I think you could solve this by adding setValidated(false) and event.target.reset() to the handleClickTransitionAlert, like this:
const handleClickTransitionAlert = (event) => {
setTransitionAlert(true);
setTimeout(() => {
setTransitionAlert(false);
setValidated(false)
event.target.reset()
}, 1700);
};

Try reseting the validated attribute on Bootsrap Form.
it should look something like this (this is pseudo-code):
import React, { useRef, useState } from 'react';
const FormComponent = () => {
const [validated, setValidated] = useState(false);
const formRef = useRef(null);
const handleReset = () => {
formRef.current.reset();
setValidated(false);
};
const handleSubmit = () => {
// Do stuff here
// On success or error:
setValidated(true);
handleReset();
}
return(
<Form ref={formRef} validated={validated} onSubmit={handleSubmit}>
// your form inputs
</Form>
);
export default FormComponent;
}

Related

React useState and onSubmit form doesn't update the state immediately - validation best practice

I'm developing a custom Form validation for my React project with Typescript.
I'm facing an issue with the useState that is not updating immediately the state containing the errors when I submit a form.
Let me provide you an example.
const initialFormState = {
email: '',
password: '',
}
const SignUpForm = () => {
const [formValues, setFormValues] = useState(initialFormState);
const [validationErrors, setValidationErrors] = useState<string>([]);
const handleChange = () => {
// handle the change implementation updating the formValues ...
}
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
doValidationOnSubmit();
if (validationErrors.length > 0) {
console.log('validation errors!!');
return;
}
doLogin();
};
const doValidationOnSubmit = () => {
Object.entries(formValues).forEach(([inputName, value]) => {
if (formValues[inputName] === '') {
setValidationErrors((oldValidationErrors) => [...oldValidationErrors, `${inputName} is not valid`]);
}
});
}
const doLogin = () => {
// do login logic
}
return (
<>
<form onSubmit={handleSubmit}>
<input type="email" name="email" onChange={handleChange} />
<input type="password" name="email" onChange={handleChange} />
<button type="submit">Login</button>
</form>
</>
);
}
export default SignUpForm;
When I'm checking for the errors in the handleSubmit, there are no errors, even if errors should be present there:
if (validationErrors.length > 0) {
console.log('validation errors!!');
return;
}
In general, I'm wondering what is the best practice in order to avoid these kinds of issues with the react state not updating immediately the state?
I already tried with useEffect, listening on the validationErrors changes but nothing changes actually, the behavior is pretty the same.
I'm sure I'm missing something.
useState is asynchronous, so state changes (setValidationErrors) are not applied immediately. Therefore, you cannot get the latest state of validationErrors in the next line.
We can do validation and set state separately. In that case, you can leverage the latest value (not the latest state) to check values validity.
const initialFormState = {
email: '',
password: '',
}
const SignUpForm = () => {
const [formValues, setFormValues] = useState(initialFormState);
const [validationErrors, setValidationErrors] = useState<string>([]);
const handleChange = () => {
// handle the change implementation updating the formValues ...
}
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
//get all invalid values
const invalidValues = returnInvalidValues();
//update state
setValidationErrors(prev => [...prev, ...invalidValues])
if (invalidValues.length > 0) {
console.log('validation errors!!');
return;
}
doLogin();
};
const returnInvalidValues = () => {
return Object.entries(formValues).filter(([inputName, value]) => formValues[inputName] === ''). map(invalidValue => `${inputName} is not valid`);
}
const doLogin = () => {
// do login logic
}
return (
<>
<form onSubmit={handleSubmit}>
<input type="email" name="email" onChange={handleChange} />
<input type="password" name="email" onChange={handleChange} />
<button type="submit">Login</button>
</form>
</>
);
}
export default SignUpForm;
You also can try useEffect
const initialFormState = {
email: '',
password: '',
}
const SignUpForm = () => {
const [formValues, setFormValues] = useState(initialFormState);
const [validationErrors, setValidationErrors] = useState<string>([]);
const handleChange = () => {
// handle the change implementation updating the formValues ...
}
//introduce useEffect here
useEffect(() => {
if (validationErrors.length > 0) {
console.log('validation errors!!');
return;
}
doLogin();
}, [validationErrors]);
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
doValidationOnSubmit();
};
const doValidationOnSubmit = () => {
Object.entries(formValues).forEach(([inputName, value]) => {
if (formValues[inputName] === '') {
setValidationErrors((oldValidationErrors) => [...oldValidationErrors, `${inputName} is not valid`]);
}
});
}
const doLogin = () => {
// do login logic
}
return (
<>
<form onSubmit={handleSubmit}>
<input type="email" name="email" onChange={handleChange} />
<input type="password" name="email" onChange={handleChange} />
<button type="submit">Login</button>
</form>
</>
);
}
export default SignUpForm;
If it does not work for your case, you can delay using the latest state with setTimeout. With this approach, it will put the task to get the latest state to the end of the call stack queue (you can check this document)
const initialFormState = {
email: '',
password: '',
}
const SignUpForm = () => {
const [formValues, setFormValues] = useState(initialFormState);
const [validationErrors, setValidationErrors] = useState<string>([]);
const handleChange = () => {
// handle the change implementation updating the formValues ...
}
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
doValidationOnSubmit();
setTimeout(() => {
if (validationErrors.length > 0) {
console.log('validation errors!!');
return;
}
doLogin();
})
};
const doValidationOnSubmit = () => {
Object.entries(formValues).forEach(([inputName, value]) => {
if (formValues[inputName] === '') {
setValidationErrors((oldValidationErrors) => [...oldValidationErrors, `${inputName} is not valid`]);
}
});
}
const doLogin = () => {
// do login logic
}
return (
<>
<form onSubmit={handleSubmit}>
<input type="email" name="email" onChange={handleChange} />
<input type="password" name="email" onChange={handleChange} />
<button type="submit">Login</button>
</form>
</>
);
}
export default SignUpForm;

Page reload onChange on type

Im trying to console.log the input value inside Input but every time i type something the page reloads. Console show no errors.
const [enteredText, setEnteredText] = useState("");
const textHandler = (event) => {
setEnteredText(event.target.value);
};
const submitHandler = (event) => {
event.preventDefault();
const textData = {
text: enteredText,
};
console.log(textData);
};
return (
<Post onSubmit={submitHandler}>
<TopPost>
<ProfilePicture></ProfilePicture>
<Input
placeholder="What's happening?"
required
onChange={textHandler}
/>
</TopPost>
<BottomPost>
<Button type="submit">Post</Button>
</BottomPost>
</Post>

Send form information to another component

I designed a flash card and I want to change the text of the card when I click on the button, but I do not know how to send the form information to the App component to update the state.
App component
function App() {
const [flashCard, setFlashCard] = useState({
word: "test",
persianEquivalent: "test",
});
const setFlashCardHandler = (e) => {
setFlashCard({ flashCard: e });
};
return (
<div className="container">
<Form setFlashCard={setFlashCardHandler} />
<FlashCard flashCard={flashCard} />
</div>
);
}
export default App;
Form component:
function Form({ setFlashCard }) {
const [valueEn, setValueEn] = useState();
const [valuePer, setValuePer] = useState();
const setValueHandlerEn = (e) => {
setValueEn(e.target.value);
};
const setValueHandlerPer = (e) => {
setValuePer(e.target.value);
};
const setFlashCardHandler = (e) => {
e.preventDefault();
setFlashCard((e)=>{valueEn=e.target[0].value});
};
return (
<form onSubmit={setFlashCardHandler}>
<input
id="word-input"
placeholder="world"
value={valueEn}
onChange={setValueHandlerEn}
/>
<input
id="persian-equivalent-input"
placeholder="Equivalent"
value={valuePer}
onChange={setValueHandlerPer}
/>
<button id="submit-btn">send</button>
</form>
);
}
export default Form;
There is an issue with how you set values.
Pass setFlashCard as the prop to Form
<Form setFlashCard={setFlashCard} />
In Form change the setFlashCardHandler as below.
const setFlashCardHandler = (e) => {
e.preventDefault();
setFlashCard({ word: valueEn, persianEquivalent: valuePer });
};
Set empty string ("") as default state to avoid sending undefined.
const [valueEn, setValueEn] = useState("");
const [valuePer, setValuePer] = useState("");
Please add new updateFlashCard props to to component.
Like:
<Form updateFlashCard={(e) => setFlashCard(e)}/>
And change value in updateFlashCard state from form component
Like:
function Form({ updateFlashCard }) {
const [valueEn, setValueEn] = useState();
const [valuePer, setValuePer] = useState();
const setValueHandlerEn = (e) => {
setValueEn(e.target.value);
};
const setValueHandlerPer = (e) => {
setValuePer(e.target.value);
};
const setFlashCardHandler = (e) => {
e.preventDefault();
updateFlashCard(e.target[0].value) // update from here
};
return (
<form onSubmit={setFlashCardHandler}>
<input
id="word-input"
placeholder="world"
value={valueEn}
onChange={setValueHandlerEn}
/>
<input
id="persian-equivalent-input"
placeholder="Equivalent"
value={valuePer}
onChange={setValueHandlerPer}
/>
<button id="submit-btn">send</button>
</form>
);
}
export default Form;

Component getting re rendered multiple times in react hooks

Since I am pretty new to react hooks, I am unable to understand some part of the existing code why my component is re-rendering multiple times when the state of an attribute gets changed. Below is the component code. I have added console.log for better understanding.
import React, { useState, useRef } from 'react';
import api from '../api/api';
import { UPLOAD_DATA } from '../api/urls';
import Alert from '../layout/alerts/Alerts';
const StudentDetailsView = ({ symbol }) => {
console.log("inside StudentDetailsView");
const initialState = {
form: {
qualification: symbol.qualification,
profession: symbol.profession
}
};
const [loading, setLoading] = useState(false);
const [errors, setErrors] = useState(null);
const [editFlag, setEditFlag] = useState(false);
const [inputs, setInputs] = useState(initialState);
console.log("before dataUpdated");
const [dataUpdated, setDataUpdated] =useState(false);
console.log("after dataUpdated");
const formRef = useRef(null);
const handleCancel = () => {
setEditFlag(false);
setInputs(initialState);
};
const handleSubmit = (e) => {
console.log("inside handleSumbit");
const form = formRef.current;
e.preventDefault();
e.stopPropagation();
form.classList.add('was-validated');
if (form.checkValidity()) {
callback();
}
};
const callback = ()=> {
setLoading(true);
const formData = new FormData();
formData.append('model', new Blob([JSON.stringify(inputs.form)], {
type: 'application/json'
}));
api.multipartEdit(UPLOAD_DATA, formData)
.then(response => {
setInputs(inputs => ({
...inputs,
form: {
qualification: response.data.qualification,
profession: response.data.profession
}
}));
setErrors(null);
setDataUpdated(true);
})
.catch(error => {
setErrors(error);
})
.finally(() => {
setLoading(false);
setEditFlag(false);
});
}
const handleInputChange = (event) => {
event.persist();
setInputs(inputs => ({
...inputs,
form: {
...inputs.form,
[event.target.name]: event.target.value
}
}));
}
return (
<div>
{
errors &&
<Alert type={errors.type} title={errors.title} description={errors.description} id="alert" />
}
<div >
{editFlag ? (
<div >
</div>
) :
(<div className="edit">
<button type="button" onClick={() => setEditFlag(!editFlag)}>
Edit
</button>
</div>)
}
</div>
<div>
<form className="needs-validation" onSubmit={handleSubmit} ref={formRef} noValidate>
{
editFlag ? (<div className="update-cancel-button">
<button className="btn btn-primary" type="submit" >
{loading ? (
<div className="spinner-border uxf-spinner-border-sm">
<span className="sr-only">Loading...</span>
</div>) : 'Update'}
</button>
<button className="btn btn-secondary cancel-button" type="button" onClick={handleCancel}>Cancel</button>
</div>) : <div></div>
}
<dl className="uxf-dl uxf-dl-horizontal">
<dt>Name</dt>
<dd>{symbol.name}</dd>
<dt>Age</dt>
<dd>{symbol.age}</dd>
<dt>Qualification</dt>
{editFlag ?
(<dd>
<textarea className="form-control" name="qualification" id="qualification"
value={inputs.form.qualification}
onChange={handleInputChange}
maxLength="255"></textarea>
<div className="invalid-feedback">
Please provide a Qualification.
</div>
</dd>)
:
(<dd>{dataUpdated ? (inputs.form.qualification ? inputs.form.qualification : '-') : (symbol.qualification ? symbol.qualification : '-')}</dd>)
}
<dt>Profession</dt>
{editFlag ?
(<dd>
<textarea className="form-control" name="profession" id="profession"
value={inputs.form.profession}
onChange={handleInputChange}
minLength="1"
maxLength="1000"
required></textarea>
<div className="invalid-feedback">
Please provide a Profession.
</div>
</dd>)
:
(<dd>{dataUpdated ? inputs.form.profession : symbol.profession}</dd>)
}
</dl>
</form>
</div>
</div>
);
}
export default StudentDetailsView;
Since my component is getting re-rendered, my state values which are getting set in the code (eg, dataUpdated) are getting updated with the default value again and again. How do I prevent this from happening? Please see the below images for better understanding.
(Showing the mockup of the edit component as the actual data was showing in the actual edit component)
I have clicked the edit button once and then clicked the cancel button once and this is the console log got generated.
Please uase spread operators on initializing or settingup states
const [inputs, setInputs] = useState({...initialState});
const handleCancel = () => {
setEditFlag(false);
setInputs({...initialState});
};

Using React hooks for the first time and ran into an issue

I am using react for the first time in months and hooks for the first time ever. So far I love it, but I ran into an issue that has blocked me for hours now.
I have a component that looks like this:
import React, { useState } from "react";
import ReactModal from "react-modal";
import { useModal } from "react-modal-hook";
import styled from 'styled-components';
export function SearchImageForm(props) {
const [query, setQuery] = useState("");
const [images, setImages] = useState("");
const handleSubmit = (e) => {
e.preventDefault();
fetch(`myurl&q=${query}`)
.then((response) => {
return response.json();
})
.then((myJson) => {
if (myJson.totalHits > 0) {
setImages(myJson.hits);
showModal(images, handleClick);
}
});
};
const FormWrapper = styled.div`
text-align: left;
`;
const Container = styled.div``;
const LabelEl = styled.label``;
const Input = styled.input``;
const handleClick = (e) => {
const urlField = document.querySelector('#url');
urlField.value = e.target.src;
urlField.select();
document.execCommand("copy");
alert('URL has been copied to your clipboard. You may now paste it into the URL field.')
};
return (
<Container>
<FormWrapper>
<form onSubmit={handleSubmit}>
<LabelEl>
Enter Search Term
<Input type="text" name="query" value={query} onChange={e => setQuery(e.target.value)} />
</LabelEl>
<input type="submit" value="Submit" />
</form>
</FormWrapper>
</Container>
);
}
export default SearchImageForm;
const ImageResult = ({content, handleClick}) => (
<picture>
<img src={content.previewURL} alt="" onClick={handleClick}/>
</picture>
);
const [showModal, hideModal] = useModal(({images, handleClick}) => (<ReactModal isOpen ariaHideApp={false}>
<p>Found {images.length} image(s). Click an image to copy its URL to your clipboard.</p>
{images.map(image => <ImageResult key={image.id} content={image} handleClick={handleClick} />)}
<input type="text" name="url" id="url" key="url" />
<button onClick={hideModal}>Hide modal</button>
</ReactModal>));
When I try to run it, I get the React hooks error (Invalid Hook Call). Any ideas on what I am doing wrong here?
Here is the code on codesandbox, though i am not sure why it wont run.
https://codesandbox.io/s/blue-firefly-0sx6h
Thanks!
The useModal hook needs to be called inside your component.
From the Invalid Hook Call Warning documentation:
Hooks can only be called inside the body of a function component.
So the modification what you need to do is the following:
export function SearchImageForm(props) {
const [showModal, hideModal] = useModal(({images, handleClick}) => (
<ReactModal isOpen ariaHideApp={false}>
<p>Found {images.length} image(s). Click an image to copy its URL to your clipboard. </p>
{images.map(image => <ImageResult key={image.id} content={image} handleClick={handleClick} />)}
<input type="text" name="url" id="url" key="url" />
<button onClick={hideModal}>Hide modal</button>
</ReactModal>
));
// ... rest of your component code
return <>{/* your render */}</>
}
I hope that helps!

Categories

Resources