Keep the Form values after refreshing the page [duplicate] - javascript

This question already has answers here:
How to maintain state after a page refresh in React.js?
(8 answers)
Closed 12 months ago.
I have created a basic form validation page using ReactJS with input fields of validation using regex.
I have a problem that when I fill the input fields with some data and before completing it if I click refresh the page the input fields are getting cleared.
I want to stop the input fields from clearing after refresh.
How can I do that.
Below is my code:
import React, { useState, useEffect } from 'react';
import PhoneIcon from '#mui/icons-material/Phone';
import LockIcon from '#mui/icons-material/Lock';
import { NavLink, useNavigate } from 'react-router-dom';
import "../index.css";
function Github () {
const initialvalues = {number:"", password:""}
const [formValues, setFormValues] = useState(initialvalues);
const [formErrors, setFormErrors] = useState({});
const [isSubmit, setIsSubmit] = useState(false);
const handleChange = (e) =>{
const{name,value} = e.target;
setFormValues({...formValues, [name]:value});
}
const handleSubmit = (e) =>{
e.preventDefault();
const errors = validate(formValues);
if (Object.keys(errors).length) {
setFormErrors(errors);
} else {
setIsSubmit(true);
}
}
let navigate = useNavigate();
const handleSubmits = (e) =>{
e.preventDefault();
const passerrors = validates(formValues);
if (Object.keys(passerrors).length) {
setFormErrors(passerrors);
} else {
navigate('/admin');
}
}
useEffect(()=>{
console.log(formErrors);
if(Object.keys(formErrors).length ===0 && !isSubmit){
console.log(formValues);
}
},[formErrors])
const validates = (values) =>{
const passerrors ={}
const regexp = /^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!##$%^&*_=+-]).{4,12}$/;
if(!values.password){
passerrors.password = "Password is required";
}else if(!regexp.test(values.password)){
passerrors.password = "passsword must contain atleast one uppercase,lowercase,number,special character";
}
else if(values.password.length < 4){
passerrors.password = "Password must me more than 4 characters";
}else if (values.password.length > 6){
passerrors.password = "Password cannot be more than 6 characters";
}
return passerrors;
}
const validate = (values) =>{
const errors = {}
const regexn = /^(\+91[-\s]?)?[0]?(91)?[789]\d{9}$/;
if(!values.number){
errors.number = "Mobile Number is required";
}else if(!regexn.test(values.number)){
errors.number = "Please enter a valid mobile number"
}else if(values.number.length > 10){
errors.number = "number must be only 10 digits"
}
return errors;
};
return (
<div className="container"
style={{textAlign:'center',paddingTop:"50px"}}>
<img src="images/img1.jpg" alt=''
width="100px" height="100px"
style={{borderRadius:"50%"}} /><br/>
<div style={{justifyContent:'space-between'}}>
<br/><br/><br/><br/>
{ !isSubmit
?
<form onSubmit={handleSubmit} >
<div>
<div>
<label style={{position:"relative", top:"8px",right:"5px",left:"115px"}}>
<PhoneIcon/></label>
<input className='input_field' type="number" name="number" placeholder='enter number'
autoComplete='off'
value={formValues.number}
onChange={handleChange}
style={{width:"180px",height:"35px"}}
/>
<label className='input_label'>Mobile Number</label>
<p style={{color:"red",fontSize:"13px"}} >{formErrors.number}</p>
</div>
<button className='btn-primary'
style={{width:"210px",height:"30px",fontSize:"15px",
marginLeft:"30px",marginTop:"20px",
backgroundColor:" rgb(0, 110, 255)"
}}>
Submit</button>
</div>
</form>
:
<form onSubmit={handleSubmits} >
<div>
<div>
<label style={{position:"relative", top:"8px",right:"5px",left:"80px"}} ><LockIcon/></label>
<input className='input_fieldp' type="password" name='password' placeholder='enter password'
value={formValues.password}
onChange={handleChange}
style={{width:"180px",height:"35px"}}
/>
<label className='input_labelp'>Password</label>
<p style={{color:"red",fontSize:"13px"}}>{formErrors.password}</p>
</div>
<button
style={{width:"210px",height:"30px",fontSize:"15px",
marginLeft:"30px",marginTop:"20px",
backgroundColor:" rgb(0, 110, 255)"
}}>
Login</button>
</div>
</form>
}
</div>
</div>
)
}
export default Github;
Suggest me how can I do that.

When you refresh you cannot use react state or redux store as they are cleared and initialized with each page refresh. The best approach I see here is to use your localstorage to store the values.
Then you can check whether the data is available in localstorage and use that on the initial render using a useEffect hook.

What you can do is use localstorage. You can use the native localstorage but i do recommend using lscache as it is more flexible (also handles if the browser doesn't have lscace) and since you're using a framework anyway.
what you can do is on your handleCange everytime you save something on state, you also save it on localstorage:
const handleChange = (e) =>{
const{name,value} = e.target;
setFormValues({...formValues, [name]:value});
// you can add a third parameter for expiration if you don't want it to be there forever
lscache.set(name, value);
}
then have a useEffect to handle the fetching of data on the cache on refresh
useEffect(() => {
const number = lscache.get("number");
setFormValues({...formValues, number});
}, [])
PS. Make sure the name of the cache is unique on the form/site.

Related

To Do list updates an empty string in the list

I have started an internship I have to build a to-do list using NEXT JS. but the problem arises that the app also updates an empty string. I have to work on this and have more than 20 hours to dig up a solution. I wasn't able to solve it. I tried passing some parameters but it's not working.
import { useState } from "react"
import '../styles/globals.css'
const index=()=> {
const [userinput,setuserinput]=useState("")
const [todolist,settodolist]=useState([])
const handlechange=(e)=>{
e.preventDefault()
if(e.target.value!=""){
setuserinput(e.target.value)
}
}
const handlesubmit=(e)=> {
settodolist([
userinput,
...todolist
])
e.preventDefault()
}
const handledelete=(todo)=>{
const updatedelete=todolist.filter(todoitem => todolist.indexOf(todoitem) != todolist.indexOf(todo))
settodolist(updatedelete)
}
return(
<div className="FLEX">
<h3 className="heading">Welcome to Next JS To Do app</h3>
<form className="FORM">
<div className="Wrap">
<input type="text" onChange={handlechange} placeholder="Enter a todo item" className="INPUT"></input>
<button onClick={handlesubmit} className="Button">Submit</button>
</div>
</form>
<ul>
{
todolist.length>=1?todolist.map((todo,idx)=>{
return <li key={idx}>{todo} <button onClick={(e)=>{
e.preventDefault()
handledelete(todo)
}}>Delete</button></li>
}):"Enter a Todo List"
}
</ul>
</div>
)
}
export default index
You need to pass the value prop to your input element:
<input type="text" value={userinput} onChange={handlechange} placeholder="Enter a todo item" className="INPUT"></input>
If you don't want the user to submit an empty item to the todo list, check if the userinput is empty or not.
const handlesubmit = (e) => {
if (userinput === "") return
settodolist([
userinput,
...todolist
])
e.preventDefault()
}
I believe the question is asking to prevent the user from entering a todo item with no name. In this case, do as the previous comment mentioned and add the value prop to the input:
<input type="text" value={userinput} onChange={handlechange} placeholder="Enter a todo item" className="INPUT"></input>
then add this to your handleSubmit function:
const handlesubmit = (e) => {
e.preventDefault();
if (userinput != '') {
settodolist([userinput, ...todolist]);
}
};

How to know if form input is empty (React Hooks)

I have a form where I want to know if the input values ​​are empty when onSubmit, they are not sent. I have tried to do it through the if of handleInputChange but this isn't working:
const handleInputChange = (e: React.FormEvent<HTMLInputElement>) => {
if ((e.target as HTMLInputElement).value) {
setNewPost({
...newPost,
[(e.target as HTMLInputElement).name]: (e.target as HTMLInputElement).value
})
}
e.preventDefault();
};
All the code:
const New: React.FC = () => {
// const [newPost, setNewPost] = useState("");
const [newPost, setNewPost] = useState({
title: '',
author: '',
content: ''
})
const handleInputChange = (e: React.FormEvent<HTMLInputElement>) => {
if ((e.target as HTMLInputElement).value) {
setNewPost({
...newPost,
[(e.target as HTMLInputElement).name]: (e.target as HTMLInputElement).value
})
}
e.preventDefault();
};
const createPost = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault(); //Detiene el formulario para que no actualize la página
setPost(newPost)
}
return (
<div className="containerHomepage">
<form className="formulari" onSubmit={createPost}>
<div className="containerBreadCrumb">
<ul className="breadCrumb">
<li>Posts</li>
</ul>
</div>
<div className="containerTitleButton">
<input
className=""
type="text"
placeholder='Post title'
name="title"
onChange={handleInputChange}
></input>
<button
className="button"
type="submit"
>Save</button>
</div>
<div className="containerEdit">
<input
className=""
type="text"
placeholder='Author'
name="author"
onChange={handleInputChange}
></input>
<input
className=""
type="text"
placeholder='Content'
name="content"
onChange={handleInputChange}
></input>
</div>
</form>
</div>
);
};
// ========================================
export default New;
Your current handleInputChange makes it so that the user cannot change any input to an empty string. There's a major usability flaw here. Once the user types the first character, they cannot delete it! You should allow the inputs to be empty, but disallow submitting the form unless all fields are filled out.
You can use e.currentTarget instead of e.target to avoid a lot of type assertions. There is more information in this question, but what's important here is that e.currentTarget will always be the HTMLInputElement.
const handleInputChange = (e: React.FormEvent<HTMLInputElement>) => {
setNewPost({
...newPost,
[e.currentTarget.name]: e.currentTarget.value
});
};
#rax's answer is on the right track, but I'm going to go further down that path.
An any point in time, you can know whether the form is valid or not by looking at the current state of newPost. There are lots of ways to write this, which all do the same thing:
const isValid = Boolean(newPost.title && newPost.author && newPost.content);
Using type coercion. All strings are truthy except for the empty string.
const isValid = newPost.title !== '' && newPost.author !== '' && newPost.content !== '';
From #Vladimir Trotsenko's answer.
const isValid = Object.values(newPost).every(value => value.length > 0)
Looping over all values of newPost so you don't need to change anything if you add an extra field.
You can use this isValid variable to conditionally disable the "Save" button.
<button type="submit" disabled={!isValid}>Save</button>
You can also use isValid to show messages or other visible feedback to the user. For example, you can show a message when hovering over the disabled button which tells them why it has been disabled.
<button
type="submit"
disabled={!isValid}
title={isValid ? "Create your post" : "All fields must be filled out."}
>
Save
</button>
I'm checking if (isValid) in the createPost function to be on the safe side, but I believe that this is not actually necessary as the form won't be submitted (even when hitting Enter) if the submit button is disabled.
const createPost = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault(); // stop the form from reloading the page.
if (isValid) {
// put your actual code here instead.
alert("submit success");
}
};
Complete code:
import React, { useState } from "react";
const New: React.FC = () => {
const initialState = {
title: "",
author: "",
content: ""
};
const [newPost, setNewPost] = useState(initialState);
const isValid = Boolean(newPost.title && newPost.author && newPost.content);
const handleInputChange = (e: React.FormEvent<HTMLInputElement>) => {
setNewPost({
...newPost,
[e.currentTarget.name]: e.currentTarget.value
});
};
const createPost = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault(); //stop the form from reloading the page
if (isValid) {
alert("submit success");
}
};
return (
<div className="containerHomepage">
<form className="formulari" onSubmit={createPost}>
<div className="containerBreadCrumb">
<ul className="breadCrumb">
<li>Posts</li>
</ul>
</div>
<div className="containerTitleButton">
<input
className=""
type="text"
placeholder="Post title"
name="title"
onChange={handleInputChange}
value={newPost.title}
/>
<button
className="button"
type="submit"
disabled={!isValid}
title={
isValid ? "Create your post" : "All fields must be filled out."
}
>
Save
</button>
</div>
<div className="containerEdit">
<input
className=""
type="text"
placeholder="Author"
name="author"
onChange={handleInputChange}
value={newPost.author}
/>
<input
className=""
type="text"
placeholder="Content"
name="content"
onChange={handleInputChange}
value={newPost.content}
/>
</div>
</form>
</div>
);
};
export default New;
CodeSandbox
You can compare your input value to empty string like that :
inputValue === ''
or the size of the string :
inputValue.length === 0
And check the value in if statement with inside your submit.
You can validate the empty field inside createPost which should look something like:
const createPost = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault(); //stop the form from reloading the page
if (!newPost.title || !newPost.author || !newPost.content) {
//show some error message
} else {
//perform further action
setPost(newPost);
}
}
For a full working example click here.
When you try to submit, firstly, you need to check whether input values are present. In order to do that you check each input value to equal empty string.
For a better visualization, create variable that would be responsible for that:
const isValid = newPost.title !== '' && newPost.author !== '' && newPost.content !== ''
Now, if isValid is true, we submit the form, if not, we do not.
const createPost = (e) => {
e.preventDefault()
if (isValid) {
// make api request
} else {
// handle empty fields
}
}

How do i make the data in the input feild of my form in next js stay after refresh of the page?

I am working on a form in nextjs and i would love the data to remain the same i.e persist after the entire page as been refreshed or reloaded . Local storage doesnt work with next js , so i am looking for an alternative , i always get local storage not defined when i use it
Here is my code below
import React, { useState, useEffect, useLayoutEffect, createContext , useContext } from "react";
import { useRouter } from "next/router";
import Cookie from "js-cookie";
import { parseCookies } from "../helpers/index";
import { Formik } from "formik";
function Form() {
return (
<div>
<form action="" >
<section class="left">
<div class="input-container">
<label for="name">Full name</label>
<input type="text"/>
</div>
<div class="input-container">
<label for="age" required>
Mobile Number
</label>
<input type="text"/>
</div>
<div class="input-container">
<label for="phone">Choose password</label>
<input type="text"/>
</div>
</div>
</section>
</form>
</div>
);
}
export default Form;
With formik out of the question, to let data persist after refresh, you need to save it to localStorage ( or cookies ).
This works for NextJS (you need to test for window first)
Example as follows
const App = () => {
const [ value, setValue ] = useState({
name: '',
mobile: ''
});
useEffect(() => {
//you need to call this for nextjs, so this is performed only on client side.
if (typeof window !== 'undefined') {
let storedValue = localStorage.getItem('value');
if (storedValue) {
storedValue = JSON.parse(storedValue) || {}
// we explicitly get name and mobile value in case localStorage was manually modified.
const name = storedValue.name || ''
const mobile = storedValue.mobile || ''
setValue({ name, mobile }) //restore value from localStorage
}
}
},[])
// alternatively a betterway to handle side effect is useEffect
// useEffect(() => {
// localStorage.setItem('value', JSON.stringify(value))
// },[value])
const onChange = (e) => {
const name = e.target.name
const newValue = { ...value, [name]: e.target.value }
setValue(newValue);
localStorage.setItem('value', JSON.stringify(newValue)) //save input to localstorage
}
return (<div>
<input name="name" value={value.name} onChange={onChange} />
<input name="mobile" value={value.mobile} onChange={onChange} />
</div>
)
}
}

React: How to submit a form with <textarea> with enter key and without a submit button with React Hooks?

Halo guys I am quite new to react and I want to submit a form with only a text area with enter key press. I followed some of the SO questions but still no luck as it is not getting submitted. I also want to clear the text area after the submit. How can I do it with the below code?
This is the code I currently have.
const { register, handleSubmit, control, errors } = useForm();
const CommentOnSubmit = (data) => {
let formData = new FormData();
formData.append('content', data.content);
formData.append('user', user?.id);
axiosInstance.post(`api/blogs/` + slug + `/comment/`, formData);
} ;
// const commentEnterSubmit = (e) => {
// if(e.key === 'Enter' && e.shiftKey == false) {
// return(
// e.preventDefault(),
// CommentOnSubmit()
// )
// }
// }
<form noValidate onSubmit={handleSubmit(CommentOnSubmit)}>
<div className="post_comment_input">
<textarea
type="text"
placeholder="Write a comment..."
name="content"
ref={register({required: true, maxLength: 1000})}
/>
</div>
<div className="comment_post_button">
<Button type="submit" variant="contained" color="primary">comment</Button>
</div>
</form>
Please do help.
Thanks a lot.
you can use React SyntheticEvent onKeyPress like this:
<textarea
type="text"
placeholder="Write a comment..."
onKeyPress={ commentEnterSubmit}
name="content"
ref={register({ required: true, maxLength: 1000 })}
/>
UPDATE
In your commentEnterSubmit function:
const commentEnterSubmit = (e) => {
if (e.key === "Enter" && e.shiftKey == false) {
const data = {content:e.target.value};
return handleSubmit(CommentOnSubmit(data));
}
And if you want to reset your input you can use the setValue from useForm hooks.
like this:
add setValue to const { register, handleSubmit, control, errors,setValue } = useForm();
const CommentOnSubmit = (data) => {
let formData = new FormData();
formData.append("content", data);
formData.append("user", user?.id);
// your axios call ...
setValue("content","");
};
I have made a code sandBox based on your example

Input email field onchange not setting string to state if last field

This is really weird, I am setting an email input as a string to state and I can see on react dev tools that it gets sent, but If I try to log it from another function I get empty string, the thing is that If I change the order of the inputs and the email is not the last one then it all works.
import React, { useState, useEffect, useCallback, useContext } from 'react'
import { useDropzone } from 'react-dropzone'
import context from '../provider/context'
import axios from 'axios'
const File = () => {
const { setStage, setProject, project, setUrls, urls, email, setEmail } = useContext(context)
const onDrop = useCallback((acceptedFiles) => uploadFile(acceptedFiles), [project])
const { getRootProps, isDragActive, getInputProps } = useDropzone({ onDrop })
// Set project name
const addProject = (e) => setProject(e.target.value)
// Set email address
const addEmail = (e) => setEmail(e.target.value)
// I got another function then that logs the `email` state,
// but if I do it right after typing on the email input I get empty string.
// If I invert the order, and `project` comes after the email input
// then it displays the email string just fine.
return (
<>
<ul className='list'>
<li>
<label className='label' htmlFor='upload'>
Project's Name
</label>
<input
id='upload'
value={project}
type='text'
name='project'
placeholder='e.g Great Project'
onChange={addProject}
autoFocus
/>
</li>
<li>
<label className='label' htmlFor='email'>
Your email address
</label>
<input
id='email'
type='email'
name='email'
value={email}
placeholder='Email address to send notification to'
onChange={addEmail}
/>
</li>
</ul>
<div className='fileTitle' {...getRootProps()}>
{isDragActive ? <p className='label'>Drop the file here ...</p> : handleResponse()}
<div className='file'>
<div id='drop-area' className={`drop-area ${isDragActive ? 'active' : ''}`}>
<div className={`icon ${response ? response : ''}`}></div>
</div>
<input
{...getInputProps()}
className='inputfile'
id='file'
type='file'
name='locations'
/>
</div>
<br />
<em className='info'>
* Don’t include any headers in the file, just your list of urls with{' '}
<strong>no headers</strong>.
</em>
</div>
</>
)}
export default File
The function that logs the email uses the react-dropzone plugin
// Upload file
const uploadFile = async (file) => {
console.log(email)
const formData = new FormData()
formData.append('urls', file[0])
try {
const options = {
headers: { 'content-type': 'multipart/form-data' },
params: { project, email }
}
const res = await axios.post('/api/upload/', formData, options)
setUrls(res.data.number)
setResponse('success')
setTimeout(() => setStage('process'), 1200)
} catch (err) {
setResponse(err.response.data)
}
}
Doing a simple onclick works fine
const checkEmail = () => {
console.log(email) // This works cause it takes it form the useContext
}
And then on the html
<button onClick={checkEmail}>Click here<button>
In the end I needed to add email as an array dependency to the react-drop zone useCallback so it can register that something has change on that state.
so I changed:
const onDrop = useCallback((acceptedFiles) =>
uploadFile(acceptedFiles), [project])
To
const onDrop = useCallback((acceptedFiles) =>
uploadFile(acceptedFiles), [project, email])
And that is the reason why, when I changed the project field after adding the email it was working.
Many thanks to #NateLevin who helped me find where the problem was at.

Categories

Resources