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

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.

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]);
}
};

Keep the Form values after refreshing the page [duplicate]

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.

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>
)
}
}

How can I update the initial state from a useState from a axios.get return

there! Well, I'm trying to build a update page to my personal project, on that page, i'd like to have all the information about a specfic spot. I'm storing the informations on a MongoDB cluster and getting it with a axios.get request.
Let me explain it better:
export default function({ history }) {
window.data = {}
const url = window.location.href.slice(-24)
useEffect(() => {
async function setParams() {
const { data } = await api.get(`/spots/${url}`)
console.log(data)
window.data = data
}
setParams()
}, [url])
console.log(window.data)
//I'd like to put the window.data data inside of this
//useStates, so I will display into the form inputs the
//previous information to the user update it in the backend.
const [thumbnail, setThumbnail] = useState(null)<---
const [company, setCompany] = useState('')<---
const [price, setPrice] = useState('')<---
const [techs, setTechs] = useState('')<---
const preview = useMemo(() => {
return thumbnail ? URL.createObjectURL(thumbnail) : null;
}, [thumbnail])
async function handleSubmit(event) {
event.preventDefault()
const data = new FormData()
const user_id = localStorage.getItem('user')
data.append('thumbnail', thumbnail)
data.append('company', company)
data.append('techs', techs)
data.append('price', price)
await api.put(`/updates/${url}`, data, { new: true })
history.push('/dashboard')
}
return (
<form onSubmit={handleSubmit}>
<label
id='thumbnail'
style={{ backgroundImage: `url(${preview})` }}
className={thumbnail ? 'has-thumbnail' : ''}
>
<input type='file' onChange={event => setThumbnail(event.target.files[0])}/>
<img src={camera} alt='Select img' />
</label>
<label htmlFor="company">EMPRESA *</label>
<input
id="company"
placeholder="Sua empresa"
value={company}//I'd like to fill this inputs with the information that I got from the get request.
onChange={event => setCompany(event.target.value)}
/>
<label htmlFor="company">TECNOLOGIAS *<span>(separadas por vírgula)</span></label>
<input
id="techs"
placeholder="Tenologias utilizadas"
value={techs}
onChange={event => setTechs(event.target.value)}
/>
<label htmlFor="company">PREÇO *</label>
<input
id="price"
placeholder="Valor por dia"
value={price}
onChange={event => setPrice(event.target.value)}
/>
<button type='submit' className='btn'>Cadastrar</button>
</form>
)
}
What I know about this is that I need to render the page or at least the useStates AFTER getting the request. I've tried many ways to do it but nothing worked.
Shoud I put everything inside a class and use Async/Await?? I don't know how to change this code to it.

component with ternary operator doesn't re-render when state is updated reactjs

I'm coding an image uploader in my admin interface. I have a form that is made for creating new meals that include several properties (name, description, etc.) but also an image that I upload and store thanks to Cloudinary.
I want a thumbnail of my image to appear once I dropped the image inside the dropzone. So I added a ternary operator that should render the thumbnail once the image is uploaded.
However, this piece of code does not re-render once the image is uploaded. the div remains empty and the thumbnail does not appear.
is there something wrong in my code ??
import React from 'react';
import Dropzone from 'react-dropzone'
import axios from 'axios'
export default class MealForm extends React.Component {
constructor() {
super();
this.state = {
mealImageURL: ""
}
this.createMeal = this.createMeal.bind(this);
}
handleDrop(files) {
const uploaders = files.map(file => {
const formData = new FormData();
formData.append("file", file);
formData.append("tags", `meal, food`);
formData.append("upload_preset", "xxxxxxx");
formData.append("api_key", "xxxxxxxxxx");
formData.append("timestamp", (Date.now() / 1000) | 0);
// Make an AJAX upload request using Axios (replace Cloudinary URL below with your own)
return axios.post("https://api.cloudinary.com/v1_1/xxxxxxx/image/upload", formData, {
headers: { "X-Requested-With": "XMLHttpRequest" },
}).then(response => {
const data = response.data;
const fileURL = data.secure_url // You should store this URL for future references in your app
this.setState({mealImageURL: fileURL});
console.log(this.state.mealImageURL);
console.log(data);
})
});
// Once all the files are uploaded
axios.all(uploaders).then(() => {
// ... perform after upload is successful operation
});
}
createMeal(e) {
e.preventDefault();
let name = this.refs.name.value.trim();
let description = this.refs.description.value.trim();
let ingredients = this.refs.ingredients.value.trim();
let allergenes = this.refs.allergenes.value.trim();
let category = this.refs.category.value.trim();
let weekDay = this.refs.weekday.value.trim();
let restaurant = this.refs.restaurant.value.trim();
let image = this.state.mealImageURL;
Accounts.createUser({}, err => {
console.log('Meal creation Callback: ', err)
})
}
render() {
return (
<form onSubmit={this.createMeal}>
<input type="text" ref="name" className="form-control" id="meal-form-name-input" aria-describedby="name" placeholder="Name" />
<textarea ref="description" className="form-control" id="meal-form-description-input" aria-describedby="description" placeholder="description" rows="3"></textarea>
<textarea ref="ingredients" className="form-control" id="meal-form-ingredients-input" aria-describedby="ingrdients" placeholder="ingredients" rows="2"></textarea>
<textarea ref="allergenes" className="form-control" id="meal-form-allergenes-input" aria-describedby="allergenes" placeholder="allergenes" rows="2"></textarea>
<input type="text" ref="category" className="form-control" id="meal-form-category-input" aria-describedby="category" placeholder="category" />
<input type="text" ref="weekday" className="form-control" id="meal-form-weekday-input" aria-describedby="week day" placeholder="week day" />
<input type="text" ref="restaurant" className="form-control" id="meal-form-restaurant-input" placeholder="restaurant" />
<div>
<div className="FileUpload">
<Dropzone
onDrop={this.handleDrop}
multiple={false}
accept="image/*"
>
<p>Drop your files or click here to upload</p>
</Dropzone>
</div>
<div> // That's my ternary operator:
{this.state.mealImageURL === '' ? null :
<div>
<p>{this.state.mealImageURL}</p>
<img src={this.state.mealImageURL} />
</div>}
</div>
</div>
<button type="submit" className="btn btn-primary">Create Meal</button>
</form>
);
}
}
you forgot this.handleDrop = this.handleDrop.bind(this); In the constructor.

Categories

Resources