React js update form value using useState - reactjs

I have a React functional component, a form accepting information for events. I need to send the filled in form info using a POST request. My formData state is not updating, I have tried different onChange functions to try and get this to work. Any ideas where I am going wrong?
`
import styled from 'styled-components';
import axios from 'axios';
import Input from './Input';
import react, {useState, useEffect} from "react";
import DateTimePicker from 'react-datetime-picker';
import TimePicker from "react-time-picker";
import DatePicker from "react-date-picker";
const url = 'http://localhost:5000/events/create'
const EventForm = (props)=> {
const [dateValue, onChangeDate] = useState(new Date());
const [timeValue, onChangeTime] = useState();
const [formData, setFormData] = useState({
firstName: '',
lastName: '',
contactEmail: '',
eventTitle: '',
eventDescription: '',
})
function onChange (e) {
let name = e.target.name ;
let value = e.target.value;
let formObj = { ...formData };
setFormData({ ...formData, [name]: value });
console.log(formData)
}
const body = {
firstName: formData.firstName,
lastName: formData.lastName,
contactEmail: formData.contactEmail,
eventTitle: formData.eventTitle,
eventDescription: formData.eventDescription,
eventDate: dateValue,
eventTime: timeValue,
}
const postFormData = async (e) => {
console.log(formData)
e.preventDefault()
await axios({
method: 'post',
url: url,
data: body,
})
.then((response) => {
console.log(response)
})
.catch(error => console.log(`Error: ${error}`));
}
// const postFormData = async (e) => {
// e.preventDefault()
// let newEvent = await fetch("http://localhost:5000/events/create",
// {
// method: "POST",
// headers: {
// 'Content-Type': 'application/json',
// 'Accept': 'application/json'
// },
// body: JSON.stringify(body)
// });
// newEvent = await newEvent.json();
// console.log(newEvent);
// }
useEffect(() => {
return () => {
console.log(formData.firstName)
}
})
return (
<form onSubmit={props.onSubmit}>
<>
{/* <DateTimePicker onChange={onChange} value={value} minDate={new Date()}/> */}
<StyledForm onSubmit={postFormData}>
<label>
First Name
</label>
<Input
name={"firstName"}
placeholder={"First Name"}
type={"text"}
value={formData.firstName}
onChange={(e) => setFormData({ ...formData, firstName: e.target.value})}
/>
<label>
Last Name
</label>
<Input
name={"lastName"}
placeholder={"Last Name"}
type={"text"}
onChange={onChange}
/>
<label>
Contact Email
</label>
<Input
name={"contactEmail"}
placeholder={"Email"}
type={"email"}
onChange={onChange}
/>
<label>
Event Date
</label>
<DatePicker onChange={onChangeDate} value={dateValue}/>
<label>
Event Time
</label>
<TimePicker onChange={onChangeTime} value={timeValue} />
<label>
Event Description
</label>
<Input
name={"eventTitle"}
placeholder={"Event Title"}
type={"text"}
onChange={onChange}
/>
<label>
Event Description
</label>
<Input
name={"eventDescription"}
placeholder={"Event Description"}
type={"text"}
width={"300px"}
height={"300px"}
onChange={onChange}
/>
<Input
name={"submit"}
type={"submit"}
value={"Create"}
/>
</form>
</>
);
}
export default EventForm;`

The formData state variable will not update immediately, your console.log will always print the original value before the update. Also when the next state is computed using the previous state like in your example, you should use the functional update version of setState:
const [formData, setFormData] = useState({
firstName: '',
lastName: '',
contactEmail: '',
eventTitle: '',
eventDescription: '',
})
function onChange (e) {
let name = e.target.name;
let value = e.target.value;
setFormData((currentFormData) => {
const nextFormData = {
...currentFormData,
[name]: value,
})
console.log(nextFormData)
return nextFormData;
);
}

Related

handleSubmit to call another function with event parameter

This is React JS.
I had a nice working sendData function that creates a new record on my json file.
It worked nice until I decided to add useForm to add some yup resolvers.
Now in the <form> tag here is onSubmit={}.
If I write here
<form onSubmit={handleSubmit(sendData(), onSubmit)}>, I get the error and nothing works as before.
enter image description here
I except to understand how handleSubmit works and how to resolve this problem.
Thanks in advance, guys!
my code:
import React, { useState } from 'react';
import { useForm } from 'react-hook-form';
import { Link, useNavigate } from 'react-router-dom';
import Confirmation from './Confirmation';
import * as yup from 'yup';
import { yupResolver } from '#hookform/resolvers/yup';
const schema = yup.object().shape({
name: yup.string().required(),
age: yup.number().positive().required(),
salary: yup.number().positive().required(),
email: yup.string().required(),
})
.required();
export default function LogIn() {
const { register, handleSubmit, formState: { errors }, } = useForm({
resolver: yupResolver(schema),
});
// for redirection
let navigate = useNavigate();
// modal for ghost mode
const [show, setShow] = useState(false);
const [details, setDetails] = useState({
name: '',
age: 0,
salary: 0,
email: ''
})
const sendData = async (event) => {
event.preventDefault()
const {name, age, salary, email} = details;
const res = await fetch("i hide the link :D",
{
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
name, age, salary, email
})
})
navigate("/main");
}
const onSubmit = (data) => {
console.log(data)
}
return (
<div>
{show && <Confirmation show={show} setShow={setShow} />}
<div className="form-center">
<h1>Few Information</h1>
<form onSubmit={handleSubmit(sendData(), onSubmit)}>
<div className="form-controll">
<input type="text" {...register('name')} placeholder="Name"
onChange={(e) => setDetails({...details,name:e.target.value})}/>
{errors.name?.message && <p>{errors.name?.message}</p>}
<input type="number" {...register('age')} placeholder="Age"
onChange={(e) => setDetails({...details,age:e.target.value})}/>
{errors.age?.message && <p>{errors.age?.message}</p>}
<input type="number" {...register('salary')} placeholder="Salary in $"
onChange={(e) => setDetails({...details,salary:e.target.value})}/>
{errors.salary?.message && <p>{errors.salary?.message}</p>}
<input type="email" {...register('email')} placeholder="Email"
onChange={(e) => setDetails({...details,email:e.target.value})}/>
{errors.email?.message && <p>{errors.email?.message}</p>}
</div>
<div className="forgot">
Don't want to share data?<br></br>
<button onClick={() => {setShow(true)}}>Ghost mode</button>
</div>
<div className="btn">
<input type='submit' value='Go' />
</div>
</form>
</div>
</div>
)
}
handleSubmit function is a wrapper for react-hook-form to manage your data inputs, validation, errors, etc.. before calling your own sendData function.
Consider doing:
export default function LogIn() {
const sendData = async (data) => {
const {name} = data;
// your post request
}
return (
<form onSubmit={handleSubmit(sendData}> // remove the useless onSubmit
<input
type="text"
{...register('name')}
placeholder="Name"
// remove the onChange prop
/>
</form>
)
}

React redux First object missing on rendering when data is entered in the text field

What am I trying to do : Trying to enter the first name and last name of a person and display all the entered name below using redux global state management.
When I try to create a new object by entering data into the input fields, the 1st item is being displayed empty both on the view page and console log and from the 2nd item, all the items are being displayed correctly. I encountered this error previously but couldn't fix it.
Actual page:
import {useDispatch} from 'react-redux';
import {useState} from 'react';
const Inc = ()=>{
const[data, setData] = useState({});
const[firstName, setFirstName] = useState('');
const[lastName, setLastName] = useState('');
const dispatch = useDispatch();
const newData={
first_name: "",
last_name : ""
}
const handleFieldFirst=async (e)=>{
await setFirstName(e.target.value);
}
const handleFieldLast= async (e)=>{
await setLastName(e.target.value);
}
const handler = async (e)=>{
e.preventDefault();
newData.first_name = firstName ;
newData.last_name = lastName ;
await setData(newData);
console.log(data);
dispatch({type: 'INC', payload : data});
document.getElementById("form1").reset();
}
return(
<form id="form1" type ="submit" name="login" >
<input onChange={e => handleFieldFirst(e)} name="first" type="text" placeholder="First name"></input>
<input onChange={e => handleFieldLast(e)} name="last" type="text" placeholder="Last name"></input>
<button onClick={e => handler(e)}>Submit</button>
</form>
);
}
export default Inc;
View page
import {useSelector} from 'react-redux';
const Disp = ()=>{
const count = useSelector(state => state.counterReducer);
return(
<div>
{count.map((d) => <li key={d.last_name}>{d.first_name} {d.last_name}</li>)}
</div>
);
}
export default Disp;
enter image description here
First of all, you have to change your code. you have to use async and await only if the function returns a promise
import { useState } from "react";
import { useDispatch } from "react-redux";
const Inc = () => {
const [data, setData] = useState({
firstName: "",
lastName: "",
});
const dispatch = useDispatch();
const handler = (e) => {
e.preventDefault();
dispatch({ type: "INC", payload: data });
setData({
firstName: "",
lastName: "",
});
};
return (
<form id="form1" type="submit" name="login">
<input
onChange={(e) =>
setData({
...data,
firstName: e.target.value,
})
}
name="first"
type="text"
placeholder="First name"
value={data.firstName}
/>
<input
onChange={(e) =>
setData({
...data,
lastName: e.target.value,
})
}
name="last"
type="text"
placeholder="Last name"
value={data.lastName}
/>
<button onClick={handler}>Submit</button>
</form>
);
};
export default Inc;
by default useSelector will return the default state (you didn't attach reducer code so I'm assuming that the reducer returns default state) so add a condition to render <li>....</li> only the length of the array is greater than 0.

React-Formik: How to create registeration form using Formik?

I am learning React.js and want to use Formik for my project. In backend I've created registration endpoint, which works fine in frontend as well with simple form. I don't know how to implement Formik instead of simple form. I can't move forward, since all the time i get the same error:
Line 121:20: 'firstName' is not defined no-undef
Line 122:19: 'lastName' is not defined no-undef
Line 123:16: 'email' is not defined no-undef
Line 124:19: 'password' is not defined no-undef
How to get rid of that error and make it work?
Here is the register component with default form, which works fine.
import React, { useState } from 'react'
const Register = () => {
const [data, setData] = useState([])
const [firstName, setFirstName] = useState('')
const [lastName, setLastName] = useState('')
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const saveRegister = () => {
fetch('http://localhost:8000/api/v1/user', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
firstName: firstName,
lastName: lastName,
email: email,
password: password,
}),
})
.then((res) => res.json())
.then((result) => {
setData(result)
console.log(result)
})
.catch((err) => console.log('error'))
}
const handleFirstName = (e) => {
setFirstName(e.target.value)
}
const handleLastName = (e) => {
setLastName(e.target.value)
}
const handleEmail = (e) => {
setEmail(e.target.value)
}
const handlePassword = (e) => {
setPassword(e.currentTarget.value)
}
const handleSubmit = (e) => {
e.preventDefault()
saveRegister()
setFirstName('')
setLastName('')
setEmail('')
setPassword('')
}
return (
<div>
<form onSubmit={handleSubmit}>
<input
type="text"
name="firsName"
onChange={handleFirstName}
value={firstName}
placeholder="firstName"
/>
<input
type="text"
name="lastName"
onChange={handleLastName}
value={lastName}
placeholder="lastName"
/>
<input
type="text"
name="email"
onChange={handleEmail}
value={email}
placeholder="email"
/>
<input
type="text"
name="password"
onChange={handlePassword}
value={password}
placeholder="password"
/>
<button type="submit">signup</button>
</form>
</div>
)
}
export default Register
In here I'm trying to implement Formik instead
import React, { useState } from 'react'
import {
Formik,
Form,
Field,
ErrorMessage,
} from 'formik'
import * as Yup from 'yup'
function Register() {
const [data, setData] = useState([])
const saveRegister = (values) => {
fetch('http://localhost:8000/api/v1/user', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
firstName: values.firstName,
lastName: values.lastName,
email: values.email,
password: values.password,
}),
})
.then((res) => res.json())
.then((result) => {
setData(result)
})
.catch((err) => console.log('error'))
}
const initialValues = {
email: '',
lastName: '',
firstName: '',
password: ''
}
const onSubmit = (values, setSubmitting) => {
setSubmitting(true);
console.log(values);
saveRegister(values)
setSubmitting(false)
}
const validationSchema = Yup.object({
firstName: Yup.string().required('Required'),
lastName: Yup.string().required('Required'),
password: Yup.string().required('Required'),
email: Yup.string()
.email('Invalid email format').required('Required'),
})
return (
<Formik
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={onSubmit}
>
{({ values, isSubmitting }) => (
<Form >
<div>
<label htmlFor='email'>E-mail</label>
<Field
type='text'
id='email'
name='email'
/>
<ErrorMessage name='firstName' />
</div>
<div>
<label htmlFor='firstName'>firstName</label>
<Field
type='text'
id='firstName'
name='firstName'
/>
<ErrorMessage name='lastName' />
</div>
<div>
<label htmlFor='lastName'>lastName</label>
<Field
type='text'
id='lastName'
name='lastName'
/>
<ErrorMessage name='lastName' />
</div>
<div >
<label htmlFor='password'>password</label>
<Field
type='password'
id='password'
name='password'
/>
<ErrorMessage name='password' />
</div>
<button type='submit'>Submit</button>
</Form>
)}
</Formik>
)
}
export default Register
<>
<Formik {{ initialValues, validationSchema, onSubmit }}>
</Formik>
</>
and your on submit method
const onSubmit = (values, setSubmitting) => {
setSubmitting(true);
console.log(values);
saveRegister(values)
setSubmitting(false)
and in your saveRegister
const saveRegister = (values) => {
values.email //and so on
}

Updating Functional Component Local State Using Data From Redux State

I'm building contact manager. When the user clicks the update button for a specific contact an action is dispatched and the "hotContact" property in the reducer's state is populated with an object. What I want is the fields of the ContactForm to be populated with the name and number of the "hotContact". However, despite the hotContact being loaded into the redux state my ContactForm component won't display the name and number of the hotContact. How can I proceed? This is what I have so far.
I tried calling setFormData in a conditional block to check if hotContact is present and loadingHotContact is false, but that just gives me an infinite re-render error.
import React, { useState } from 'react';
import { connect } from 'react-redux';
import { addContact, updateContact } from '../actions/contacts';
const ContactForm = ({
addContact,
updateContact,
contacts: { hotContact, loadingHotContact },
}) => {
const [formData, setFormData] = useState({
name:
hotContact === null && loadingHotContact
? ''
: hotContact.name,
number:
hotContact === null && loadingHotContact
? ''
: hotContact.number,
});
const onFormDataChange = (event) => {
setFormData({ ...formData, [event.target.name]: event.target.value });
};
const { name, number } = formData;
const handleSubmit = (event) => {
event.preventDefault();
const newContact = { name, number };
addContact(newContact);
console.log('Submit the form!');
setFormData({ name: '', number: '' });
};
const handleUpdateSubmit = (event) => {
event.preventDefault();
const updatedContact = { name, number };
updateContact(hotContact._id, updatedContact);
};
return !hotContact ? (
<form onSubmit={handleSubmit}>
<div>
Name{' '}
<input
type='text'
name='name'
value={name}
onChange={(event) => onFormDataChange(event)}
/>
</div>
<div>
Number{' '}
<input
type='text'
name='number'
value={number}
onChange={(event) => onFormDataChange(event)}
/>
</div>
<input type='submit' value='Add Contact' />
</form>
) : (
<form onSubmit={handleUpdateSubmit}>
<div>
Name{' '}
<input
type='text'
name='name'
value={name}
onChange={(event) => onFormDataChange(event)}
/>
</div>
<div>
Number{' '}
<input
type='text'
name='number'
value={number}
onChange={(event) => onFormDataChange(event)}
/>
</div>
<input type='submit' value='Apply Changes' />
</form>
);
};
const mapStateToProps = (state) => ({
contacts: state.contacts,
});
export default connect(mapStateToProps, { addContact, updateContact })(
ContactForm
);
This doesn't work because at the first renderer useState is initialized with the hotContact from the props, but when you receive the new value from the props the state doesn't update (that's how the useState hook works)
If you want to update your state you should use the useEffect hook:
const ContactForm = ({
addContact,
updateContact,
contacts: { hotContact, loadingHotContact },
}) => {
const [formData, setFormData] = useState({
name:
hotContact === null && loadingHotContact
? ''
: hotContact.name,
number:
hotContact === null && loadingHotContact
? ''
: hotContact.number,
});
useEffect(() => {
const {name, number} = props.hotContact;
setFormData({
name: name || '',
number: number || '',
});
// execute this
}, [hotContact]); // when hotContact changes
}
Also, I think you may simplify you assignment this way:
const {name, number} = props.hotContact;
setFormData({
name: name || '',
number: number || '',
});

React Input Warning: A component is changing a controlled input of type text to be uncontrolled

I am practicing REST API by using one Fake API site. For front-end, I am using React typescript and React router dom for routing. I successfully login the email and password by using Fake API's login and redirect to list users, where I fetched the data from Fake API and shows the user's name, image. I used the edit button, after clicking the button it will redirect to my Update components where it will populate the input field then I will update the data. My update components work fine as expected but in my console, I am getting a warning as soon as I type my input field.Here is the Error visualization
This is React Update components
import React, { useState, useEffect } from "react";
import axios from "axios";
const Update = props => {
const [state, setState] = useState({
first_name: "",
last_name: "",
email: ""
});
const [loading, setLoading] = useState(false);
useEffect(() => {
axios
.get("https://reqres.in/api/users/" + props.match.params.id)
.then(response => {
setState({
first_name: response.data.data.first_name,
last_name: response.data.data.last_name,
email: response.data.data.email
});
})
.catch(function(error) {
console.log(error);
});
}, [props.match.params.id]);
const onChangeFirstName = e => {
setState({
first_name: e.target.value
});
};
const onChangeLastName = e => {
setState({
last_name: e.target.value
});
};
const onChangeEmail = e => {
setState({
email: e.target.value
});
};
const onSubmit = e => {
e.preventDefault();
setLoading(true);
const obj = {
first_name: state.first_name,
last_name: state.last_name,
email: state.email
};
axios
.patch("https://reqres.in/api/users/" + props.match.params.id, obj)
.then(res => console.log(res.data));
setLoading(false);
props.history.push("/users");
};
return (
<div>
<form onSubmit={onSubmit}>
<div className="form-group">
<label>First Name: </label>
<input
type="text"
className="form-control"
value={state.first_name}
onChange={onChangeFirstName}
id="first_name"
/>
</div>
<div className="form-group">
<label>Last Name: </label>
<input
type="text"
className="form-control"
value={state.last_name}
onChange={onChangeLastName}
id="last_name"
/>
</div>
<div className="form-group">
<label>Email: </label>
<input
type="email"
className="form-control"
value={state.email}
onChange={onChangeEmail}
id="email"
/>
</div>
<div className="form-group">
<button
className="btn waves-effect blue lighten-1"
type="submit"
name="action"
disabled={loading}
>
{loading ? "loading..." : "save"}
</button>
</div>
</form>
</div>
);
};
export default Update;
With hooks, when you set the state of an object, you need to merge all the properties by yourself. In other words, if you update a property of an object with state updater, the remaining properties of the objects are not merged by themselves unlike this.setState in class components.
Modify your onChange to like this:
const onChangeFirstName = e => {
const val = e.target.value;
setState(prevState => ({
...prevState,
first_name: val
}));
};
See working demo
Also quick suggestion:
Instead of writing multiple onChanges, you can simplify and just use one.
Like this:
<input
type="text"
className="form-control"
value={state.first_name}
onChange={onChange}
id="first_name"
name="first_name" />
...
const onChange = e => {
const {name, value} = e.target;
setState(prevState => ({
...prevState,
[name]: value
}));
};

Resources